mirror of
https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
synced 2026-09-26 22:51:56 +00:00
Compare commits
930
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
261caeb23e | ||
|
|
62f49b91ec | ||
|
|
0901306cf5 | ||
|
|
2dd47e8c70 | ||
|
|
3e89a23166 | ||
|
|
d48692bd4b | ||
|
|
9fa9fb078e | ||
|
|
e170a5cb5a | ||
|
|
a6794907e4 | ||
|
|
fed657b8fb | ||
|
|
c2ca773c27 | ||
|
|
228cad82c2 | ||
|
|
d83ef25cc6 | ||
|
|
a6439ecb5e | ||
|
|
2cbebe604c | ||
|
|
432ce2aeb1 | ||
|
|
654907ad4d | ||
|
|
8d0ad548b2 | ||
|
|
0bb5343dca | ||
|
|
97a18feba0 | ||
|
|
7200238f4f | ||
|
|
d9058c2e4e | ||
|
|
1b6e663af0 | ||
|
|
5887f400c8 | ||
|
|
5e509423a6 | ||
|
|
bb61d92f80 | ||
|
|
34431b6511 | ||
|
|
ec3ec5a2c1 | ||
|
|
d9f5a40088 | ||
|
|
713a54815b | ||
|
|
06c87bc24d | ||
|
|
75c37416d8 | ||
|
|
ad99d0d0c0 | ||
|
|
c7b3e67989 | ||
|
|
2f7078e6a8 | ||
|
|
393bb67645 | ||
|
|
b945549ec4 | ||
|
|
d963893088 | ||
|
|
7ffbf0cde7 | ||
|
|
fb978444f7 | ||
|
|
de35525c70 | ||
|
|
feb0e6410d | ||
|
|
6b1c0b35e8 | ||
|
|
04540495f0 | ||
|
|
ad6e554984 | ||
|
|
ee52b0487c | ||
|
|
89b494b96d | ||
|
|
5cc8751c20 | ||
|
|
0a22ad7578 | ||
|
|
792313cce5 | ||
|
|
27e87047bb | ||
|
|
145b2cab33 | ||
|
|
ca65becfe3 | ||
|
|
eeb5a9f6e4 | ||
|
|
c5f11d35d5 | ||
|
|
91beb065ff | ||
|
|
c22fe20973 | ||
|
|
e97e5ed7cc | ||
|
|
be98093169 | ||
|
|
ea8bc3f993 | ||
|
|
bcacc0d0d4 | ||
|
|
656afd3cff | ||
|
|
e6ae1b185c | ||
|
|
b0fdf128c3 | ||
|
|
99c4f80558 | ||
|
|
71436766d2 | ||
|
|
2ad5331658 | ||
|
|
83d231ada8 | ||
|
|
6479533e29 | ||
|
|
6e7ec746d1 | ||
|
|
efd08571df | ||
|
|
44c868c4b4 | ||
|
|
59f7fa0fd9 | ||
|
|
2c3f2736eb | ||
|
|
ed54360fc7 | ||
|
|
66506d4269 | ||
|
|
099876c411 | ||
|
|
bfd36eecbb | ||
|
|
b82ade00bb | ||
|
|
68f3500196 | ||
|
|
74b5b8ce59 | ||
|
|
6627f58dac | ||
|
|
31fdffd56a | ||
|
|
15bec0d5ba | ||
|
|
f0a2b16f75 | ||
|
|
3c6a5b3245 | ||
|
|
21ba3b0f71 | ||
|
|
d15a635917 | ||
|
|
ce18d2a32a | ||
|
|
12cbd5a082 | ||
|
|
86fbbdbe01 | ||
|
|
191424175e | ||
|
|
c544933d44 | ||
|
|
fff8aad453 | ||
|
|
3c3c924917 | ||
|
|
a874825e61 | ||
|
|
10ca70f9b3 | ||
|
|
b640558e09 | ||
|
|
b5169f96b7 | ||
|
|
b7a3c3faed | ||
|
|
9ae5ce3276 | ||
|
|
bce7d73867 | ||
|
|
708861b55c | ||
|
|
93f36606a2 | ||
|
|
373795f9f8 | ||
|
|
d9bb986997 | ||
|
|
048336b6d2 | ||
|
|
9340737a8e | ||
|
|
e801ede0c8 | ||
|
|
7fce82b608 | ||
|
|
20440b6418 | ||
|
|
ef69968160 | ||
|
|
9bbe7c6643 | ||
|
|
84875c4814 | ||
|
|
28fb284400 | ||
|
|
441e4e7965 | ||
|
|
52d1fe548e | ||
|
|
71921231dc | ||
|
|
0553f768eb | ||
|
|
9a0037bb64 | ||
|
|
68aad86385 | ||
|
|
520a0b0ae8 | ||
|
|
8e817a4ece | ||
|
|
7391eed85c | ||
|
|
578b118329 | ||
|
|
4bd9d4c54b | ||
|
|
c97aedf227 | ||
|
|
6bb255bc5f | ||
|
|
5a96b4a335 | ||
|
|
b502254a10 | ||
|
|
ec6ba9ecf0 | ||
|
|
709745f405 | ||
|
|
0b1046e6c6 | ||
|
|
f6ae1205dc | ||
|
|
ab98cf8b36 | ||
|
|
c60e8d1e3d | ||
|
|
f1e3b02eb0 | ||
|
|
5885cb3724 | ||
|
|
9827ae0218 | ||
|
|
712a066981 | ||
|
|
5528d1cedc | ||
|
|
155f316588 | ||
|
|
50d13e004d | ||
|
|
03a2717248 | ||
|
|
3d7e0c2861 | ||
|
|
de54c3d2dd | ||
|
|
d3e066a1ee | ||
|
|
669eb0f2e7 | ||
|
|
3618721baf | ||
|
|
9fad2cf0eb | ||
|
|
9b625c143b | ||
|
|
6600309c4a | ||
|
|
1091ec83b4 | ||
|
|
a29e1dd50a | ||
|
|
ef7959a7d8 | ||
|
|
2d38e7fc3b | ||
|
|
5ac40786f1 | ||
|
|
91ea9c7d4e | ||
|
|
7de2195b1c | ||
|
|
a9ba2062b1 | ||
|
|
de46d815b9 | ||
|
|
6e4fb5396a | ||
|
|
3d9a462528 | ||
|
|
d0de4f9340 | ||
|
|
fb42792527 | ||
|
|
cfaa10834c | ||
|
|
5dfc3c29d0 | ||
|
|
91aa0822b9 | ||
|
|
b002e7ba2c | ||
|
|
c273a4c3a9 | ||
|
|
07ae15d2e2 | ||
|
|
d3e1ede5d2 | ||
|
|
010d294439 | ||
|
|
8b019191b6 | ||
|
|
f4cf4339a8 | ||
|
|
fad5a00754 | ||
|
|
c94be301cb | ||
|
|
d219a0cae7 | ||
|
|
879422219c | ||
|
|
ae884f48ba | ||
|
|
c52e657c88 | ||
|
|
6da5cb1d74 | ||
|
|
fb56adc633 | ||
|
|
8304d32e76 | ||
|
|
811f06f992 | ||
|
|
f092fce2db | ||
|
|
c26a7d19c7 | ||
|
|
e7baf06290 | ||
|
|
114a113cb3 | ||
|
|
e78068bb79 | ||
|
|
d940c93f47 | ||
|
|
9b336a72f3 | ||
|
|
dfec25bde7 | ||
|
|
3fe0889b9a | ||
|
|
5df1dfcc37 | ||
|
|
2303f110da | ||
|
|
9ba17b5227 | ||
|
|
6ea28621ac | ||
|
|
5c2bd2a13a | ||
|
|
675a98a005 | ||
|
|
3d2518b177 | ||
|
|
cc1a25733b | ||
|
|
2ac6a917af | ||
|
|
3440690fa8 | ||
|
|
1a4373c3c4 | ||
|
|
529ad0dd8e | ||
|
|
beaef16d62 | ||
|
|
80d25ef87f | ||
|
|
f21ac67fb2 | ||
|
|
8be5a069eb | ||
|
|
721269538d | ||
|
|
240811979e | ||
|
|
435e58884d | ||
|
|
f716600b44 | ||
|
|
c7c573bae3 | ||
|
|
ee7dd9b5d8 | ||
|
|
42956223f2 | ||
|
|
3b736a24a9 | ||
|
|
1ab839ac47 | ||
|
|
fe7d3e4b8e | ||
|
|
2c987c1949 | ||
|
|
3f7b3292c1 | ||
|
|
aaba9fe4e1 | ||
|
|
aa04d7a60d | ||
|
|
b17709785d | ||
|
|
5362c4ff61 | ||
|
|
4ce6dbc450 | ||
|
|
a5270054b2 | ||
|
|
30fe41eb2e | ||
|
|
17b898bd15 | ||
|
|
6415b1406d | ||
|
|
bbfddc2dfe | ||
|
|
59da0b7fa4 | ||
|
|
321a7c7deb | ||
|
|
386c2f7ab9 | ||
|
|
feb14aa36c | ||
|
|
44e0c77ddb | ||
|
|
cec6321027 | ||
|
|
3cde0c8eab | ||
|
|
16c010a859 | ||
|
|
11bad82468 | ||
|
|
9a7197a774 | ||
|
|
c638e6d983 | ||
|
|
5651427a97 | ||
|
|
6195e7bbf9 | ||
|
|
2b4f7834b8 | ||
|
|
6eadb9199d | ||
|
|
cf9ba17c7d | ||
|
|
384fa2c1b6 | ||
|
|
4d24324cc6 | ||
|
|
b1331406a0 | ||
|
|
cf00491bdc | ||
|
|
b24de2c375 | ||
|
|
79d80a1f4f | ||
|
|
de3e31e037 | ||
|
|
d45d5fad51 | ||
|
|
10a9e776b7 | ||
|
|
db80aa4953 | ||
|
|
f4345ed935 | ||
|
|
b191b93c7e | ||
|
|
8acf15cccb | ||
|
|
8cd372c2d1 | ||
|
|
447d79a165 | ||
|
|
3d461a8458 | ||
|
|
228e2034f7 | ||
|
|
fd3cc2a2cc | ||
|
|
76a1f49aaa | ||
|
|
cb41dfc8bd | ||
|
|
9b5e734dca | ||
|
|
274ed4ef49 | ||
|
|
92498e8148 | ||
|
|
ce48820cc1 | ||
|
|
b59e74be8a | ||
|
|
47579bdb10 | ||
|
|
761ffc7b17 | ||
|
|
99cd99b385 | ||
|
|
be783a2ed1 | ||
|
|
7523b575ab | ||
|
|
4f5a637cd0 | ||
|
|
0701ca01a8 | ||
|
|
78e0a67cd9 | ||
|
|
4ef75656a1 | ||
|
|
f5299da9b0 | ||
|
|
b986daa72d | ||
|
|
e987dce2ad | ||
|
|
f2f98cc8ed | ||
|
|
0661fcc121 | ||
|
|
f4c0fc7781 | ||
|
|
8e3db693cb | ||
|
|
0349a9552d | ||
|
|
ac1f03dd71 | ||
|
|
cb18a37b0a | ||
|
|
4c8ec05f22 | ||
|
|
c17df33c7c | ||
|
|
543cd66c16 | ||
|
|
f50dac63eb | ||
|
|
df979c1c65 | ||
|
|
7b49c23eb6 | ||
|
|
965835122b | ||
|
|
54a34a7122 | ||
|
|
a701f3b08c | ||
|
|
59a220cb58 | ||
|
|
098ad42f70 | ||
|
|
1f141d0059 | ||
|
|
e0a1608783 | ||
|
|
1a469a09df | ||
|
|
bc3f6d3819 | ||
|
|
f088a3ce69 | ||
|
|
863660042b | ||
|
|
7162248267 | ||
|
|
2e908a8efa | ||
|
|
588d2c880e | ||
|
|
54f10c7411 | ||
|
|
0c2b9e9f45 | ||
|
|
fe07d416e5 | ||
|
|
9c69cbfd54 | ||
|
|
2a25be6af2 | ||
|
|
9680a7e772 | ||
|
|
30527875ed | ||
|
|
8c11e19d86 | ||
|
|
f44f51c06d | ||
|
|
840b537ea6 | ||
|
|
d2602b944f | ||
|
|
bb39135946 | ||
|
|
cb72a56e55 | ||
|
|
eee97362d0 | ||
|
|
a92c5be0e9 | ||
|
|
80707a3ba7 | ||
|
|
903f69b81c | ||
|
|
40a31daaef | ||
|
|
37ba322412 | ||
|
|
ee43400e89 | ||
|
|
176c3721fd | ||
|
|
1d6f9bc36b | ||
|
|
973ecf95e7 | ||
|
|
1f2adec703 | ||
|
|
9843c1f063 | ||
|
|
5afdc3524a | ||
|
|
770508b4f0 | ||
|
|
987881e887 | ||
|
|
8e53b623e5 | ||
|
|
948537e1d4 | ||
|
|
4338b1d90b | ||
|
|
b7a41637ea | ||
|
|
c8f7b910f2 | ||
|
|
1e226a3419 | ||
|
|
287f70abc2 | ||
|
|
8e9d664d88 | ||
|
|
166a407b8d | ||
|
|
72f9ba7647 | ||
|
|
24549bc506 | ||
|
|
dc4efb0c63 | ||
|
|
b8c6cc29b4 | ||
|
|
3b6d2e02d0 | ||
|
|
f33adcea80 | ||
|
|
5b6063b8a8 | ||
|
|
1857b23556 | ||
|
|
86d85ca555 | ||
|
|
67b50fda2c | ||
|
|
0d5cb22491 | ||
|
|
c030d1d79c | ||
|
|
702e6fc262 | ||
|
|
3b2c58821e | ||
|
|
6b302d6ac8 | ||
|
|
78b2aa87f3 | ||
|
|
0b9582341c | ||
|
|
2869cdb021 | ||
|
|
7b2e54bbfb | ||
|
|
32ada1378b | ||
|
|
2581d90588 | ||
|
|
aa3aa7335f | ||
|
|
7028fa896e | ||
|
|
9e599ac03f | ||
|
|
284fabb30e | ||
|
|
aee8d9fa86 | ||
|
|
91606af0f0 | ||
|
|
81ffae5a44 | ||
|
|
c1c95e5e3c | ||
|
|
7bd3814dd9 | ||
|
|
66f3d8497d | ||
|
|
8d80062253 | ||
|
|
b9fff2e5e8 | ||
|
|
df6ffb7a48 | ||
|
|
84d7b17098 | ||
|
|
7ce3015958 | ||
|
|
0aafebdff3 | ||
|
|
eaddeb62d7 | ||
|
|
3b919c1e7d | ||
|
|
ff6a43cbad | ||
|
|
c03b0343d0 | ||
|
|
bbabed68b8 | ||
|
|
858fed1b07 | ||
|
|
2bbb773eef | ||
|
|
0d8df106ef | ||
|
|
749eb6eb74 | ||
|
|
71d01b8dcd | ||
|
|
56a0605ba1 | ||
|
|
368152fdcb | ||
|
|
86e9323847 | ||
|
|
ce90f9b07d | ||
|
|
a6450646bd | ||
|
|
7117ab3023 | ||
|
|
5486fae2e6 | ||
|
|
83047c3604 | ||
|
|
416ec5081c | ||
|
|
72cd14c7f9 | ||
|
|
23f7217a5e | ||
|
|
81df7b2103 | ||
|
|
db8e4aa3b1 | ||
|
|
5834bdf57b | ||
|
|
25484d8244 | ||
|
|
77b33d1f54 | ||
|
|
71376764a4 | ||
|
|
5252708bff | ||
|
|
58fe261d12 | ||
|
|
3932db4033 | ||
|
|
3d276d9bd4 | ||
|
|
3a09e269a8 | ||
|
|
50efecfaab | ||
|
|
4c68aff8b3 | ||
|
|
1f8f05d6d9 | ||
|
|
bd03ae7831 | ||
|
|
d131ab5874 | ||
|
|
865c2fb868 | ||
|
|
5cca6edccd | ||
|
|
ccdb24c145 | ||
|
|
90480c3be5 | ||
|
|
943df70b47 | ||
|
|
3637c8b3d7 | ||
|
|
80fe1e5e02 | ||
|
|
1a59543d01 | ||
|
|
e99629c42e | ||
|
|
dd774e1f02 | ||
|
|
31dd31e3d4 | ||
|
|
ca61199c03 | ||
|
|
3e0a6634a6 | ||
|
|
e45cfa6d16 | ||
|
|
dfabe38846 | ||
|
|
6b9a54d59e | ||
|
|
2b1f97b1da | ||
|
|
65f5a95ac5 | ||
|
|
0441a3792e | ||
|
|
3828455354 | ||
|
|
d38dfe79d9 | ||
|
|
4ccc40db70 | ||
|
|
330886448b | ||
|
|
1990422749 | ||
|
|
34eda2a7ca | ||
|
|
f5e380a08e | ||
|
|
4b173f1f6f | ||
|
|
752be49136 | ||
|
|
282ecdfd39 | ||
|
|
802357f65b | ||
|
|
e11598ca5a | ||
|
|
bf72ac6312 | ||
|
|
f9019ed15e | ||
|
|
20dcdd3054 | ||
|
|
b566021678 | ||
|
|
5b2f4c2534 | ||
|
|
9a61e3c722 | ||
|
|
a56efdcec7 | ||
|
|
c98d3df75a | ||
|
|
6247fbb96f | ||
|
|
ad339286b0 | ||
|
|
430d789c8f | ||
|
|
94eef657ee | ||
|
|
4b5ada9a44 | ||
|
|
5c22ed4eaa | ||
|
|
36fcc6355c | ||
|
|
4c881849e2 | ||
|
|
7d1f7650b9 | ||
|
|
0183abfdd2 | ||
|
|
b92337699a | ||
|
|
4dcc5413cf | ||
|
|
5f47ba8023 | ||
|
|
698503e73d | ||
|
|
e1a15c4bc9 | ||
|
|
0260d79703 | ||
|
|
16712e53ba | ||
|
|
401064a06c | ||
|
|
28d29b4691 | ||
|
|
20902244de | ||
|
|
ff5939aa8b | ||
|
|
ea23ffd42a | ||
|
|
fb6527f66a | ||
|
|
55f8a6f78a | ||
|
|
397285f4bf | ||
|
|
ea3167b8f7 | ||
|
|
c3526504d8 | ||
|
|
9a409b9011 | ||
|
|
c9cca725c6 | ||
|
|
3ddc77293b | ||
|
|
7fa90ee179 | ||
|
|
b88a775d33 | ||
|
|
d58718ce27 | ||
|
|
f380b42d49 | ||
|
|
98be4d8cb4 | ||
|
|
765d6ee296 | ||
|
|
d08959b1a0 | ||
|
|
aec5fbfd6f | ||
|
|
bcba9b5ea2 | ||
|
|
fe42cb6ebd | ||
|
|
7c90baf6e3 | ||
|
|
f9be4f470d | ||
|
|
309889bf6b | ||
|
|
8eabca5939 | ||
|
|
830a762d2d | ||
|
|
be95016723 | ||
|
|
0a7a2f6eeb | ||
|
|
c0196a16b8 | ||
|
|
8e53879df1 | ||
|
|
8ecc2c25c6 | ||
|
|
d98d427271 | ||
|
|
b35c2a89cb | ||
|
|
bc93be1651 | ||
|
|
baa9c06cf7 | ||
|
|
3f3ef75aba | ||
|
|
1a2a0f1d50 | ||
|
|
93a099f65b | ||
|
|
b6804cdf78 | ||
|
|
b641e0857e | ||
|
|
e12faf03ed | ||
|
|
d24f0d0f21 | ||
|
|
c50d38e82f | ||
|
|
52f458fd7d | ||
|
|
eeaf34aa3b | ||
|
|
705f64dc32 | ||
|
|
3830e14fd6 | ||
|
|
bd55db1efc | ||
|
|
07c0f3710f | ||
|
|
f9de0b6315 | ||
|
|
5052d1f44d | ||
|
|
35fbd744e2 | ||
|
|
6d394e639c | ||
|
|
540410ba89 | ||
|
|
26e6548988 | ||
|
|
16de6f1b99 | ||
|
|
8cb2e868ce | ||
|
|
3153e24e57 | ||
|
|
f40a81dda7 | ||
|
|
1590d1cc6f | ||
|
|
6b065f1c5a | ||
|
|
b88fddd6bb | ||
|
|
22454b5318 | ||
|
|
e26190b5e3 | ||
|
|
e6ecd23556 | ||
|
|
a342923353 | ||
|
|
0adcf3d60c | ||
|
|
49547be529 | ||
|
|
9dbd6303b0 | ||
|
|
ac049f3de1 | ||
|
|
3bca163dea | ||
|
|
8904b43308 | ||
|
|
828926e9ab | ||
|
|
851dfb72c1 | ||
|
|
dea652ceca | ||
|
|
56310240b3 | ||
|
|
d828534f28 | ||
|
|
5b6340a15e | ||
|
|
2d3a490aca | ||
|
|
764ea292e5 | ||
|
|
867410462e | ||
|
|
1874743d19 | ||
|
|
b19fcc9f66 | ||
|
|
12db1f9e05 | ||
|
|
853b5c0a97 | ||
|
|
00aa9a2672 | ||
|
|
7eb4eea074 | ||
|
|
4b8b1e503b | ||
|
|
db0f7fb6d7 | ||
|
|
cd04f66ac0 | ||
|
|
419e01d1d3 | ||
|
|
e7a51b394b | ||
|
|
fdfec862be | ||
|
|
6da196a80b | ||
|
|
280f62a1d3 | ||
|
|
20e524dda5 | ||
|
|
b0c7c70b81 | ||
|
|
dcfc30edab | ||
|
|
03fea0608d | ||
|
|
6c15941242 | ||
|
|
1bd5c364f5 | ||
|
|
1c6e4a36c1 | ||
|
|
080d1819ed | ||
|
|
5679e46a12 | ||
|
|
d54b3845fe | ||
|
|
6a2d06f8f4 | ||
|
|
38a37ae8b9 | ||
|
|
be20a635e4 | ||
|
|
db06465158 | ||
|
|
b42b6c6fb8 | ||
|
|
73fbc762fe | ||
|
|
994a86d07a | ||
|
|
71c1eca210 | ||
|
|
20cdcefea3 | ||
|
|
26db26d100 | ||
|
|
01575ae76d | ||
|
|
1c431bdb85 | ||
|
|
096a5d069e | ||
|
|
d795b6e1f5 | ||
|
|
34eaf50f2c | ||
|
|
08ffe85ddf | ||
|
|
3ade1ab265 | ||
|
|
6e71605669 | ||
|
|
f8af22386c | ||
|
|
9d7a744924 | ||
|
|
4f8a527f5c | ||
|
|
4a987f5dcb | ||
|
|
a2a3de5767 | ||
|
|
3ca5d6cad6 | ||
|
|
9117fbbb71 | ||
|
|
607c2605fa | ||
|
|
3509bbc383 | ||
|
|
8593308244 | ||
|
|
806801b4ea | ||
|
|
a1b1ff9a6b | ||
|
|
3acd72ec81 | ||
|
|
12347f3ce2 | ||
|
|
f9c4e32088 | ||
|
|
78da2206b0 | ||
|
|
46fa993732 | ||
|
|
df0c0d209b | ||
|
|
2e8d6239df | ||
|
|
1841201fee | ||
|
|
f10857f299 | ||
|
|
1e4b3aefdb | ||
|
|
e3bcff62fc | ||
|
|
ce3a439d06 | ||
|
|
b47d4b46f3 | ||
|
|
1cc87860c4 | ||
|
|
5d6aa4479d | ||
|
|
5a25b06f2d | ||
|
|
bb055ed061 | ||
|
|
974610a555 | ||
|
|
d78574e640 | ||
|
|
ff367ae9f5 | ||
|
|
40b5e74645 | ||
|
|
dee509e8d7 | ||
|
|
fd5921fa32 | ||
|
|
c74a44a4a8 | ||
|
|
367c985642 | ||
|
|
f7a970e15b | ||
|
|
9a914a5af4 | ||
|
|
e5315be85a | ||
|
|
6daf663a69 | ||
|
|
45a65f1db8 | ||
|
|
240c291728 | ||
|
|
4aec14c576 | ||
|
|
f7d4d3a9c3 | ||
|
|
3c64a8aa58 | ||
|
|
5f692ea299 | ||
|
|
ab1e97ac18 | ||
|
|
78630ae2c0 | ||
|
|
0f48628782 | ||
|
|
93e9fbac92 | ||
|
|
231b2ef02b | ||
|
|
7132c12831 | ||
|
|
2b547e8279 | ||
|
|
8b84524244 | ||
|
|
60fe1bd2c6 | ||
|
|
0edea80ffa | ||
|
|
493e50a999 | ||
|
|
8440e7f164 | ||
|
|
906aa91fe7 | ||
|
|
34e282172f | ||
|
|
bddf642b58 | ||
|
|
25b89c497f | ||
|
|
23a51dbcaa | ||
|
|
26761198d7 | ||
|
|
5b4e88e791 | ||
|
|
83cc796687 | ||
|
|
3e29cc62c7 | ||
|
|
2b1bb77f4b | ||
|
|
7144b87f01 | ||
|
|
ebf4d6d8be | ||
|
|
2ccd913e2e | ||
|
|
d777dd8625 | ||
|
|
d912d3d4d8 | ||
|
|
bafe623590 | ||
|
|
152077a823 | ||
|
|
81c94f9711 | ||
|
|
897e8e3e47 | ||
|
|
ddc125da69 | ||
|
|
abb66ade41 | ||
|
|
2915824641 | ||
|
|
44447637bd | ||
|
|
6a8345446d | ||
|
|
27a7b8b1da | ||
|
|
04471e7104 | ||
|
|
e78ea4e805 | ||
|
|
7487a14783 | ||
|
|
757d0c5087 | ||
|
|
60cb93a9a9 | ||
|
|
0fb72773f4 | ||
|
|
5d2fc74f78 | ||
|
|
1cc26481b2 | ||
|
|
85b0346eab | ||
|
|
910cbacb54 | ||
|
|
c7206e3cc8 | ||
|
|
12d721d33e | ||
|
|
16df2216d1 | ||
|
|
fb26b7213c | ||
|
|
d90f52e122 | ||
|
|
17f42b3044 | ||
|
|
652f34b814 | ||
|
|
c84f8738f8 | ||
|
|
9d72bbf237 | ||
|
|
85df50d06f | ||
|
|
9ab6cddade | ||
|
|
02ffffaeb7 | ||
|
|
dfc5635d34 | ||
|
|
90b686cf69 | ||
|
|
ad63153778 | ||
|
|
7858f8644e | ||
|
|
3df4098364 | ||
|
|
ae3c1877e4 | ||
|
|
2798ab1e02 | ||
|
|
7bd2c567a1 | ||
|
|
56503cce6a | ||
|
|
a5a9f53a32 | ||
|
|
469e8436e9 | ||
|
|
e9f9a5c29b | ||
|
|
102400a67f | ||
|
|
0ae53f47d4 | ||
|
|
ff3e0b4784 | ||
|
|
0f4546c18c | ||
|
|
ee381e15f5 | ||
|
|
4ddeeb290d | ||
|
|
7e99b07440 | ||
|
|
bc1ecd6260 | ||
|
|
f662ffb3e1 | ||
|
|
c40c0e5247 | ||
|
|
bd3a0f5af0 | ||
|
|
99f318333b | ||
|
|
9d36e837bb | ||
|
|
11e7ba47a4 | ||
|
|
399427c2de | ||
|
|
1a3cbb4cf0 | ||
|
|
0f116ab253 | ||
|
|
304000d719 | ||
|
|
36df462615 | ||
|
|
909f771bfd | ||
|
|
e9bef3542d | ||
|
|
037a3b041a | ||
|
|
f55e6c60cd | ||
|
|
e7e7a8e22c | ||
|
|
2cd815c640 | ||
|
|
b1c0cc9a9d | ||
|
|
af63d1c0e2 | ||
|
|
c51a82febb | ||
|
|
8e6e4729aa | ||
|
|
0e97a83836 | ||
|
|
f6645e0125 | ||
|
|
52385a6071 | ||
|
|
2365d733c4 | ||
|
|
71e6423066 | ||
|
|
139ed95ffc | ||
|
|
cafd192417 | ||
|
|
0d7ec7cd60 | ||
|
|
7fd934045a | ||
|
|
4e82877269 | ||
|
|
6ef111144d | ||
|
|
e9b8aa02e9 | ||
|
|
ac1af33c8c | ||
|
|
5bc18b01e4 | ||
|
|
e98b9d6eb4 | ||
|
|
5e6b8bf597 | ||
|
|
5b39e7d995 | ||
|
|
c4f08589a9 | ||
|
|
04c6ff4ec7 | ||
|
|
bcb0b19dc4 | ||
|
|
5586fd7c4d | ||
|
|
30e747b966 | ||
|
|
080e2b5bb5 | ||
|
|
e908774b5b | ||
|
|
60e4416a7e | ||
|
|
b207b270b4 | ||
|
|
497e93aba1 | ||
|
|
9a6c36d016 | ||
|
|
c889a57c9e | ||
|
|
dbe5d61929 | ||
|
|
c180408f41 | ||
|
|
869b19d342 | ||
|
|
3d967b180d | ||
|
|
49710a9225 | ||
|
|
9d31463585 | ||
|
|
bcd86a3707 | ||
|
|
9335ea3591 | ||
|
|
0f7343feee | ||
|
|
12cd965ce6 | ||
|
|
4e5c75fed6 | ||
|
|
b28f941abe | ||
|
|
32632711ff | ||
|
|
d676d87cce | ||
|
|
153a8044b8 | ||
|
|
c0d9250416 | ||
|
|
fd16e39f91 | ||
|
|
1551ca9435 | ||
|
|
80fcd2904f | ||
|
|
e974c034ba | ||
|
|
f0112cfc9a | ||
|
|
d2d4493397 | ||
|
|
33e7d18502 | ||
|
|
6628866130 | ||
|
|
0cd6146a6c | ||
|
|
b61395f465 | ||
|
|
649b209577 | ||
|
|
506a6e66d7 | ||
|
|
5bd6cfe6a7 | ||
|
|
ca27881ca7 | ||
|
|
5fa0ed6185 | ||
|
|
14f58e30c5 | ||
|
|
6af94b51aa | ||
|
|
2f818117db | ||
|
|
04fe89c556 | ||
|
|
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 | ||
|
|
f681879a8a | ||
|
|
b5b65198a6 | ||
|
|
3a5a14f1d8 | ||
|
|
157f8538ed | ||
|
|
532bf04933 | ||
|
|
99547ccb73 | ||
|
|
2e0cd74533 | ||
|
|
b9a9d76e8b | ||
|
|
fb61e0631c | ||
|
|
16c38c8fbf | ||
|
|
8888e8ad7f | ||
|
|
c787a0e99e | ||
|
|
8ea77a7cb0 | ||
|
|
2bb6d6deb2 | ||
|
|
7b235c935a | ||
|
|
3c88e9284c | ||
|
|
e967b02a22 | ||
|
|
aeb87cbc44 | ||
|
|
1f39a8f892 | ||
|
|
d2a6508379 | ||
|
|
39d1646b3c | ||
|
|
4c5ede0130 | ||
|
|
7715f79807 | ||
|
|
7c8eeeb1d1 | ||
|
|
6caffbe5c3 | ||
|
|
99929ca018 | ||
|
|
6aadabd967 | ||
|
|
9b5be742c1 | ||
|
|
63d5b5e3bf | ||
|
|
1448645ba4 | ||
|
|
3d19ffb131 | ||
|
|
e01174f169 | ||
|
|
62c903f47d | ||
|
|
da77846b88 | ||
|
|
7c123e68d3 | ||
|
|
9f26e8385d | ||
|
|
0fffaee6dc | ||
|
|
c34b7ab651 | ||
|
|
d57d9be9d4 | ||
|
|
88f3ecf567 | ||
|
|
ecfbeaa8ce | ||
|
|
3e5a319455 | ||
|
|
f35ce3432a | ||
|
|
2e9ff4a941 | ||
|
|
61fbd37fa4 | ||
|
|
474e4e602b | ||
|
|
e409cf710b | ||
|
|
e9bac09dce | ||
|
|
b799aad08a | ||
|
|
4d6bd1afff | ||
|
|
b4040ff01d | ||
|
|
c29e4fde26 | ||
|
|
c6fdad745f | ||
|
|
3c60c2cb97 | ||
|
|
278a014817 | ||
|
|
89ec43c706 | ||
|
|
ba6be7ee99 |
@@ -1,5 +1,14 @@
|
||||
from typing import List
|
||||
from ratemate import RateLimit
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--dry_run",
|
||||
type=bool,
|
||||
default=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
from resource_cleanup_manager import (
|
||||
DatasetResourceCleanupManager,
|
||||
ModelResourceCleanupManager,
|
||||
@@ -7,6 +16,14 @@ from resource_cleanup_manager import (
|
||||
ResourceCleanupManager,
|
||||
MatchingEngineIndexEndpointResourceCleanupManager,
|
||||
MatchingEngineIndexResourceCleanupManager,
|
||||
FeatureStoreCleanupManager,
|
||||
PipelineJobCleanupManager,
|
||||
TrainingJobCleanupManager,
|
||||
HyperparameterTuningCleanupManager,
|
||||
BatchPredictionJobCleanupManager,
|
||||
ExperimentCleanupManager,
|
||||
BucketCleanupManager,
|
||||
ArtifactRegistryCleanupManager
|
||||
)
|
||||
|
||||
rate_limit = RateLimit(max_count=25, per=60, greedy=False)
|
||||
@@ -23,7 +40,6 @@ def run_cleanup_managers(managers: List[ResourceCleanupManager], is_dry_run: boo
|
||||
try:
|
||||
if not manager.is_deletable(resource):
|
||||
continue
|
||||
|
||||
if is_dry_run:
|
||||
resource_name = manager.resource_name(resource)
|
||||
print(f"Will delete '{type_name}': {resource_name}")
|
||||
@@ -36,9 +52,7 @@ def run_cleanup_managers(managers: List[ResourceCleanupManager], is_dry_run: boo
|
||||
print("")
|
||||
|
||||
|
||||
is_dry_run = False
|
||||
|
||||
if is_dry_run:
|
||||
if args.dry_run:
|
||||
print("Starting cleanup in dry run mode...")
|
||||
|
||||
# List of all cleanup managers
|
||||
@@ -48,6 +62,14 @@ managers: List[ResourceCleanupManager] = [
|
||||
ModelResourceCleanupManager(), # ModelResourceCleanupManager must follow EndpointResourceCleanupManager due to deployed models blocking model deletion.
|
||||
MatchingEngineIndexEndpointResourceCleanupManager(),
|
||||
MatchingEngineIndexResourceCleanupManager(),
|
||||
FeatureStoreCleanupManager(),
|
||||
PipelineJobCleanupManager(),
|
||||
TrainingJobCleanupManager(),
|
||||
HyperparameterTuningCleanupManager(),
|
||||
BatchPredictionJobCleanupManager(),
|
||||
ExperimentCleanupManager(), # Experiment missing _resource_noun
|
||||
BucketCleanupManager(),
|
||||
ArtifactRegistryCleanupManager()
|
||||
]
|
||||
|
||||
run_cleanup_managers(managers=managers, is_dry_run=is_dry_run)
|
||||
run_cleanup_managers(managers=managers, is_dry_run=args.dry_run)
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
'''
|
||||
READ FIRST BEFORE MAKING CHANGES
|
||||
- Create a convention for resources created from vertex-ai-samples GH. We already have one IIRC
|
||||
- Only delete those objects as part of our clean-up script.
|
||||
- Don't run any tests on python-docs-samples-tests project, especially ones that affect resources created outside of our purview
|
||||
- Add --dry-run option to the clean-up script. This option will just output the list of resources the script will delete instead of actually deleting the resources.
|
||||
- Have a larger conversation in DEE before touching any resources that were not created as part of vertex-ai-samples
|
||||
'''
|
||||
import os
|
||||
import abc
|
||||
from typing import Any, Type
|
||||
|
||||
from google.cloud import aiplatform
|
||||
from google.cloud.aiplatform import base
|
||||
from google.cloud import storage
|
||||
from proto.datetime_helpers import DatetimeWithNanoseconds
|
||||
|
||||
# If a resource was updated within this number of seconds, do not delete.
|
||||
@@ -69,7 +79,7 @@ class VertexAIResourceCleanupManager(ResourceCleanupManager):
|
||||
def delete(self, resource):
|
||||
resource.delete()
|
||||
|
||||
def get_seconds_since_modification(self, resource: Any) -> bool:
|
||||
def get_seconds_since_modification(self, resource: Any) -> float:
|
||||
update_time = resource.update_time
|
||||
current_time = DatetimeWithNanoseconds.now(tz=update_time.tzinfo)
|
||||
return (current_time - update_time).total_seconds()
|
||||
@@ -97,13 +107,10 @@ class EndpointResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.Endpoint
|
||||
|
||||
def delete(self, resource):
|
||||
# TODO: Remove this once https://github.com/googleapis/python-aiplatform/issues/1441 is fixed
|
||||
resource._sync_gca_resource()
|
||||
for deployed_model_id in [
|
||||
models.id for models in resource._gca_resource.deployed_models
|
||||
]:
|
||||
resource._undeploy(deployed_model_id=deployed_model_id)
|
||||
|
||||
resource.delete(force=True)
|
||||
|
||||
|
||||
@@ -117,3 +124,137 @@ class MatchingEngineIndexResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
|
||||
class MatchingEngineIndexEndpointResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.MatchingEngineIndexEndpoint
|
||||
|
||||
def delete(self, resource):
|
||||
resource.undeploy_all()
|
||||
resource.delete(force=True)
|
||||
|
||||
class FeatureStoreCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.Featurestore
|
||||
|
||||
def resource_name(self, resource: Any) -> str:
|
||||
return resource.name
|
||||
|
||||
class PipelineJobCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.PipelineJob
|
||||
|
||||
class TrainingJobCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.training_jobs._CustomTrainingJob
|
||||
|
||||
job_types = [
|
||||
aiplatform.AutoMLImageTrainingJob,
|
||||
aiplatform.AutoMLTextTrainingJob,
|
||||
aiplatform.AutoMLTabularTrainingJob,
|
||||
aiplatform.AutoMLVideoTrainingJob,
|
||||
aiplatform.AutoMLForecastingTrainingJob,
|
||||
aiplatform.CustomJob,
|
||||
aiplatform.CustomTrainingJob,
|
||||
aiplatform.CustomContainerTrainingJob,
|
||||
aiplatform.CustomPythonPackageTrainingJob
|
||||
]
|
||||
|
||||
def list(self) -> Any:
|
||||
return [
|
||||
job
|
||||
for job_type in self.job_types
|
||||
for job in job_type.list()
|
||||
]
|
||||
|
||||
class HyperparameterTuningCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.HyperparameterTuningJob
|
||||
|
||||
|
||||
class BatchPredictionJobCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.BatchPredictionJob
|
||||
|
||||
class ExperimentCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.Experiment
|
||||
|
||||
@property
|
||||
def type_name(self) -> str:
|
||||
return "Experiment"
|
||||
|
||||
def resource_name(self, resource: Any) -> str:
|
||||
return resource.name
|
||||
|
||||
def get_seconds_since_modification(self, resource: Any) -> float:
|
||||
update_time = resource._metadata_context.update_time
|
||||
current_time = DatetimeWithNanoseconds.now()
|
||||
return float(current_time.timestamp() - update_time.timestamp())
|
||||
|
||||
class BucketCleanupManager(ResourceCleanupManager):
|
||||
vertex_ai_resource = storage.bucket.Bucket
|
||||
|
||||
def list(self) -> Any:
|
||||
storage_client = storage.Client()
|
||||
return list(storage_client.list_buckets())
|
||||
|
||||
def delete(self, resource):
|
||||
try:
|
||||
resource.delete(force=True)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
@property
|
||||
def type_name(self) -> str:
|
||||
return "Bucket"
|
||||
|
||||
def get_seconds_since_modification(self, resource: Any) -> float:
|
||||
# Bucket has no last_update property, only time created
|
||||
created_time = resource.time_created
|
||||
current_time = DatetimeWithNanoseconds.now()
|
||||
return float(current_time.timestamp() - created_time.timestamp())
|
||||
|
||||
def resource_name(self, resource: Any) -> str:
|
||||
return resource.name
|
||||
|
||||
def is_deletable(self, resource: Any) -> bool:
|
||||
time_difference = self.get_seconds_since_modification(resource)
|
||||
|
||||
if not self.resource_name(resource).startswith('your-bucket-name'):
|
||||
print(f"Skipping '{resource}' not a Vertex AI notebook bucket")
|
||||
return False
|
||||
|
||||
# Check that it wasn't created too recently, to prevent race conditions
|
||||
if time_difference <= RESOURCE_UPDATE_BUFFER_IN_SECONDS:
|
||||
print(
|
||||
f"Skipping '{resource}' due to update_time being '{time_difference}', which is less than '{RESOURCE_UPDATE_BUFFER_IN_SECONDS}'."
|
||||
)
|
||||
return False
|
||||
return True
|
||||
|
||||
class ArtifactRegistryCleanupManager(ResourceCleanupManager):
|
||||
vertex_ai_resource = "Artifact Registry"
|
||||
|
||||
def list(self) -> Any:
|
||||
import subprocess
|
||||
|
||||
result = subprocess.run(["gcloud artifacts repositories list --location=us-central1"],
|
||||
shell=True, capture_output=True, text=True)
|
||||
|
||||
ret = []
|
||||
lines = result.stdout.split('\n')[2:]
|
||||
for line in lines:
|
||||
repo = line.split(' ')[0]
|
||||
if repo.startswith("my-docker-repo"):
|
||||
ret.append(repo)
|
||||
|
||||
return ret
|
||||
|
||||
def delete(self, resource):
|
||||
os.system(f"! gcloud artifacts repositories delete {resource} --location=us-central1")
|
||||
|
||||
@property
|
||||
def type_name(self) -> str:
|
||||
return "ArtifactRepository"
|
||||
|
||||
def resource_name(self, resource: Any) -> str:
|
||||
return resource
|
||||
|
||||
# delete repository regardless of age
|
||||
def get_seconds_since_modification(self, resource: Any) -> float:
|
||||
return RESOURCE_UPDATE_BUFFER_IN_SECONDS + 1
|
||||
|
||||
def is_deleteable(self, resource: Any) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
import argparse
|
||||
import pathlib
|
||||
import os
|
||||
|
||||
import execute_changed_notebooks_helper
|
||||
|
||||
@@ -39,6 +40,19 @@ parser.add_argument(
|
||||
help="The path to the file that has newline-limited folders of notebooks that should be tested.",
|
||||
required=True,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--test_percent",
|
||||
type=int,
|
||||
help="The percent of notebooks to be tested (between 1 and 100).",
|
||||
required=False,
|
||||
default=100,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--build_id",
|
||||
type=str,
|
||||
help="The build id (which may be a Cloud Build job specific or user explicit.",
|
||||
required=True
|
||||
)
|
||||
parser.add_argument(
|
||||
"--base_branch",
|
||||
help="The base git branch to diff against to find changed files.",
|
||||
@@ -107,24 +121,60 @@ parser.add_argument(
|
||||
default=True,
|
||||
help="Should run notebooks in parallel.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--concurrent_notebooks",
|
||||
type=int,
|
||||
help="Maximum number of parallel notebook executions per minute",
|
||||
default=10,
|
||||
required=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry_run",
|
||||
type=str2bool,
|
||||
default=False,
|
||||
help="Dry run for testing - no execution",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
notebooks = execute_changed_notebooks_helper.get_changed_notebooks(
|
||||
changed_notebooks = execute_changed_notebooks_helper.get_changed_notebooks(
|
||||
test_paths_file=args.test_paths_file,
|
||||
base_branch=args.base_branch,
|
||||
)
|
||||
|
||||
execute_changed_notebooks_helper.process_and_execute_notebooks(
|
||||
notebooks=notebooks,
|
||||
container_uri=args.container_uri,
|
||||
staging_bucket=args.staging_bucket,
|
||||
artifacts_bucket=args.artifacts_bucket,
|
||||
should_parallelize=args.should_parallelize,
|
||||
timeout=args.timeout,
|
||||
variable_project_id=args.variable_project_id,
|
||||
variable_region=args.variable_region,
|
||||
variable_service_account=args.variable_service_account,
|
||||
variable_vpc_network=args.variable_vpc_network,
|
||||
private_pool_id=args.private_pool_id,
|
||||
|
||||
results_bucket = f"{args.artifacts_bucket}"
|
||||
# artifacts_bucket may get set by trigger to a full gs:// folder path
|
||||
if results_bucket.startswith("gs://"):
|
||||
results_bucket = results_bucket[5:]
|
||||
results_bucket = results_bucket.split('/')[0]
|
||||
results_file = f"build_results/{args.build_id}.json"
|
||||
|
||||
if args.test_percent == 100:
|
||||
notebooks = changed_notebooks
|
||||
accumulative_results = {}
|
||||
else:
|
||||
accumulative_results = execute_changed_notebooks_helper.load_results(results_bucket, results_file)
|
||||
|
||||
notebooks = [changed_notebook for changed_notebook in changed_notebooks if execute_changed_notebooks_helper.select_notebook(changed_notebook, accumulative_results, args.test_percent)]
|
||||
|
||||
if args.dry_run:
|
||||
print("Dry run ...\n")
|
||||
for notebook in notebooks:
|
||||
print(f"Would execute: {notebook}")
|
||||
else:
|
||||
execute_changed_notebooks_helper.process_and_execute_notebooks(
|
||||
notebooks=notebooks,
|
||||
container_uri=args.container_uri,
|
||||
staging_bucket=args.staging_bucket,
|
||||
artifacts_bucket=args.artifacts_bucket,
|
||||
results_file=results_file,
|
||||
should_parallelize=args.should_parallelize,
|
||||
timeout=args.timeout,
|
||||
variable_project_id=args.variable_project_id,
|
||||
variable_region=args.variable_region,
|
||||
variable_service_account=args.variable_service_account,
|
||||
variable_vpc_network=args.variable_vpc_network,
|
||||
private_pool_id=args.private_pool_id,
|
||||
concurrent_notebooks=args.concurrent_notebooks,
|
||||
)
|
||||
|
||||
@@ -21,18 +21,21 @@ import json
|
||||
import git
|
||||
import operator
|
||||
import os
|
||||
import io
|
||||
import json
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import random
|
||||
from google.cloud import storage
|
||||
import utils
|
||||
from typing import List, Optional
|
||||
from typing import List, Optional, Dict, Any
|
||||
from utils import util
|
||||
|
||||
import execute_notebook_helper
|
||||
import execute_notebook_remote
|
||||
import nbformat
|
||||
from google.cloud.devtools.cloudbuild_v1.types import BuildOperationMetadata
|
||||
from ratemate import RateLimit
|
||||
from tabulate import tabulate
|
||||
from utils import NotebookProcessors, util
|
||||
|
||||
@@ -40,6 +43,9 @@ from utils import NotebookProcessors, util
|
||||
WORKER_TIMEOUT_BUFFER_IN_SECONDS: int = 60 * 60
|
||||
PYTHON_VERSION = "3.9" # Set default python version
|
||||
|
||||
# rolling time window for accumulating build results for selecting notebooks
|
||||
MAX_RESULTS_AGE_SECONDS: int = (60 * 60) * 24 * 60 # 60 days
|
||||
|
||||
|
||||
def format_timedelta(delta: datetime.timedelta) -> str:
|
||||
"""Formats a timedelta duration to [N days] %H:%M:%S format"""
|
||||
@@ -65,7 +71,9 @@ def format_timedelta(delta: datetime.timedelta) -> str:
|
||||
@dataclasses.dataclass
|
||||
class NotebookExecutionResult:
|
||||
name: str
|
||||
path: str
|
||||
duration: datetime.timedelta
|
||||
start_time: datetime.datetime
|
||||
is_pass: bool
|
||||
log_url: str
|
||||
output_uri: str
|
||||
@@ -81,6 +89,75 @@ class NotebookExecutionResult:
|
||||
return None
|
||||
|
||||
|
||||
def load_results(results_bucket: str,
|
||||
results_file: str) -> Dict[str, Any]:
|
||||
'''
|
||||
Load accumulated notebook test results
|
||||
'''
|
||||
|
||||
print("Loading existing accumulative results ...")
|
||||
accumulative_results = {}
|
||||
try:
|
||||
client = storage.Client()
|
||||
bucket = client.bucket(results_bucket)
|
||||
|
||||
build_results_dir = os.path.dirname(results_file)
|
||||
blobs = client.list_blobs(results_bucket, prefix=build_results_dir)
|
||||
for blob in blobs:
|
||||
time_created = blob.time_created.replace(tzinfo=None)
|
||||
if (datetime.datetime.now().replace(tzinfo=None) - time_created).total_seconds() > MAX_RESULTS_AGE_SECONDS:
|
||||
continue
|
||||
|
||||
content = util.download_blob_into_memory(results_bucket, blob.name, download_as_text=True)
|
||||
|
||||
try:
|
||||
build_results = json.loads(content)
|
||||
except:
|
||||
continue # skip corrupted build results files
|
||||
for notebook in build_results:
|
||||
if notebook in accumulative_results:
|
||||
accumulative_results[notebook]['passed'] += build_results[notebook]['passed']
|
||||
accumulative_results[notebook]['failed'] += build_results[notebook]['failed']
|
||||
else:
|
||||
accumulative_results[notebook] = build_results[notebook]
|
||||
|
||||
print(accumulative_results)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# If there are no accumulative results, an empty dict is returned
|
||||
return accumulative_results
|
||||
|
||||
def select_notebook(changed_notebook: str,
|
||||
accumulative_results: Dict[str, Any],
|
||||
test_percent: int) -> bool:
|
||||
'''
|
||||
Algorithm to randomly select a notebook, but weight the propbability of selected based on past failures
|
||||
'''
|
||||
|
||||
if changed_notebook in accumulative_results:
|
||||
pass_count = accumulative_results[changed_notebook]['passed']
|
||||
fail_count = accumulative_results[changed_notebook]['failed']
|
||||
else:
|
||||
pass_count = 1
|
||||
fail_count = 0
|
||||
|
||||
inferred_failure_rate = fail_count / (pass_count + fail_count)
|
||||
|
||||
# If failure rate is high, the chance of testing should be higher
|
||||
should_test_due_to_failure = random.uniform(0, 1) <= inferred_failure_rate
|
||||
|
||||
# Additionally, only test a percentage of these
|
||||
should_test_due_to_random_subset = random.uniform(0, 1) <= (test_percent / 100)
|
||||
|
||||
if should_test_due_to_failure or should_test_due_to_random_subset:
|
||||
print(f"Selected: {changed_notebook}, {should_test_due_to_failure}, {should_test_due_to_random_subset}")
|
||||
return True
|
||||
else:
|
||||
print(f"Not Selected: {changed_notebook}, pass {pass_count}, fail {fail_count}")
|
||||
return False
|
||||
|
||||
|
||||
def _process_notebook(
|
||||
notebook_path: str,
|
||||
variable_project_id: str,
|
||||
@@ -156,7 +233,6 @@ def _create_tag(filepath: str) -> str:
|
||||
return tag
|
||||
|
||||
|
||||
rate_limit = RateLimit(max_count=50, per=60, greedy=True)
|
||||
|
||||
|
||||
def process_and_execute_notebook(
|
||||
@@ -172,7 +248,6 @@ def process_and_execute_notebook(
|
||||
notebook: str,
|
||||
should_get_tail_logs: bool = False,
|
||||
) -> NotebookExecutionResult:
|
||||
rate_limit.wait() # wait before creating the task
|
||||
|
||||
print(f"Running notebook: {notebook}")
|
||||
|
||||
@@ -191,7 +266,9 @@ def process_and_execute_notebook(
|
||||
|
||||
result = NotebookExecutionResult(
|
||||
name=tag,
|
||||
path=notebook,
|
||||
duration=datetime.timedelta(seconds=0),
|
||||
start_time=datetime.datetime.now(),
|
||||
is_pass=False,
|
||||
output_uri=notebook_output_uri,
|
||||
log_url="",
|
||||
@@ -201,7 +278,6 @@ def process_and_execute_notebook(
|
||||
)
|
||||
|
||||
# TODO: Handle cases where multiple notebooks have the same name
|
||||
time_start = datetime.datetime.now()
|
||||
operation = None
|
||||
try:
|
||||
# Get the python version for running the notebook if specified
|
||||
@@ -245,11 +321,12 @@ def process_and_execute_notebook(
|
||||
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.duration = datetime.datetime.now() - result.start_time
|
||||
result.is_pass = True
|
||||
print(f"{notebook} PASSED in {format_timedelta(result.duration)}.")
|
||||
|
||||
except Exception as error:
|
||||
result.error_message = str(error)
|
||||
|
||||
@@ -268,7 +345,7 @@ def process_and_execute_notebook(
|
||||
except Exception as error:
|
||||
result.error_message = str(error)
|
||||
|
||||
result.duration = datetime.datetime.now() - time_start
|
||||
result.duration = datetime.datetime.now() - result.start_time
|
||||
result.is_pass = False
|
||||
|
||||
print(
|
||||
@@ -336,12 +413,44 @@ def get_changed_notebooks(
|
||||
|
||||
return notebooks
|
||||
|
||||
def _save_results(results: List[NotebookExecutionResult],
|
||||
artifacts_bucket: str,
|
||||
results_file: str):
|
||||
|
||||
artifacts_bucket = artifacts_bucket.replace("gs://", "").split('/')[0]
|
||||
|
||||
print("Updating build results ...")
|
||||
build_results = {}
|
||||
for result in results:
|
||||
if result.is_pass:
|
||||
pass_count = 1
|
||||
fail_count = 0
|
||||
else:
|
||||
pass_count = 0
|
||||
fail_count = 1
|
||||
build_results[result.path] = {
|
||||
'duration': result.duration.total_seconds(),
|
||||
'start_time': str(result.start_time),
|
||||
'passed': pass_count,
|
||||
'failed': fail_count
|
||||
}
|
||||
print(f"adding {result.path}")
|
||||
|
||||
print("Saving accumulative results ...")
|
||||
content = json.dumps(build_results)
|
||||
|
||||
client = storage.Client()
|
||||
bucket = client.get_bucket(artifacts_bucket)
|
||||
bucket.blob(str(results_file)).upload_from_string(content, 'text/json')
|
||||
|
||||
|
||||
|
||||
def process_and_execute_notebooks(
|
||||
notebooks: List[str],
|
||||
container_uri: str,
|
||||
staging_bucket: str,
|
||||
artifacts_bucket: str,
|
||||
results_file: str,
|
||||
should_parallelize: bool,
|
||||
timeout: int,
|
||||
variable_project_id: str,
|
||||
@@ -349,6 +458,7 @@ def process_and_execute_notebooks(
|
||||
variable_service_account: str,
|
||||
variable_vpc_network: Optional[str] = None,
|
||||
private_pool_id: Optional[str] = None,
|
||||
concurrent_notebooks: Optional[int] = 10,
|
||||
):
|
||||
"""
|
||||
Run the notebooks that exist under the folders defined in the test_paths_file.
|
||||
@@ -369,6 +479,8 @@ def process_and_execute_notebooks(
|
||||
Required. The GCS staging bucket to write source code to.
|
||||
artifacts_bucket (str):
|
||||
Required. The GCS staging bucket to write executed notebooks to.
|
||||
results_file (str):
|
||||
Required: The path to the artifacts bucket to save results
|
||||
variable_project_id (str):
|
||||
Required. The value for PROJECT_ID to inject into notebooks.
|
||||
variable_region (str):
|
||||
@@ -377,6 +489,7 @@ def process_and_execute_notebooks(
|
||||
Required. Should run notebooks in parallel using a thread pool as opposed to in sequence.
|
||||
timeout (str):
|
||||
Required. Timeout string according to https://cloud.google.com/build/docs/build-config-file-schema#timeout.
|
||||
concurrent_notebooks (int): Max number of notebooks per minute to run in parallel.
|
||||
"""
|
||||
|
||||
# Calculate deadline
|
||||
@@ -393,7 +506,9 @@ def process_and_execute_notebooks(
|
||||
print(
|
||||
"Running notebooks in parallel, so no logs will be displayed. Please wait..."
|
||||
)
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
|
||||
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=concurrent_notebooks) as executor:
|
||||
print(f"Max workers: {executor._max_workers}")
|
||||
|
||||
notebook_execution_results = list(
|
||||
@@ -471,7 +586,7 @@ def process_and_execute_notebooks(
|
||||
print("=" * 100)
|
||||
|
||||
build_id = results_sorted[0].build_id
|
||||
logs_bucket_name = (results_sorted[0].logs_bucket).removeprefix("gs://")
|
||||
logs_bucket_name = (results_sorted[0].logs_bucket).replace("gs://", "")
|
||||
log_file_name = f"log-{build_id}.txt"
|
||||
|
||||
log_contents = util.download_blob_into_memory(
|
||||
@@ -489,6 +604,10 @@ def process_and_execute_notebooks(
|
||||
else:
|
||||
print(log_contents)
|
||||
|
||||
_save_results(results_sorted,
|
||||
artifacts_bucket,
|
||||
results_file)
|
||||
|
||||
print("\n=== END RESULTS===\n")
|
||||
|
||||
total_notebook_duration = functools.reduce(
|
||||
|
||||
@@ -36,7 +36,7 @@ steps:
|
||||
- -c
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 .cloud-build/execute_changed_notebooks_cli.py --test_paths_file "${_TEST_PATHS_FILE}" --base_branch "${_FORCED_BASE_BRANCH}" --container_uri ${_PYTHON_IMAGE} --staging_bucket ${_GCS_STAGING_BUCKET} --artifacts_bucket ${_GCS_STAGING_BUCKET}/executed_notebooks/PR_${_PR_NUMBER}/BUILD_${BUILD_ID} --variable_project_id ${PROJECT_ID} --variable_region ${_GCP_REGION} --variable_service_account ${_GCP_SERVICE_ACCOUNT} --variable_vpc_network "${_GPC_VPC_NETWORK_NAME}" `if [ ! -z "${_PRIVATE_POOL_NAME}" ]; then echo "--private_pool_id ${_PRIVATE_POOL_NAME}"; fi`
|
||||
python3 .cloud-build/execute_changed_notebooks_cli.py --test_paths_file "${_TEST_PATHS_FILE}" --base_branch "${_FORCED_BASE_BRANCH}" --container_uri ${_PYTHON_IMAGE} --staging_bucket ${_GCS_STAGING_BUCKET} --artifacts_bucket ${_GCS_STAGING_BUCKET}/executed_notebooks/PR_${_PR_NUMBER}/BUILD_${BUILD_ID} --variable_project_id ${PROJECT_ID} --variable_region ${_GCP_REGION} --variable_service_account ${_GCP_SERVICE_ACCOUNT} --variable_vpc_network "${_GPC_VPC_NETWORK_NAME}" `if [ ! -z "${_PRIVATE_POOL_NAME}" ]; then echo "--private_pool_id ${_PRIVATE_POOL_NAME}"; fi` --build_id ${BUILD_ID} --test_percent=${_TEST_PERCENT} --concurrent_notebooks=${_CONCURRENT_NOTEBOOKS}
|
||||
env:
|
||||
- 'IS_TESTING=1'
|
||||
timeout: 86400s
|
||||
|
||||
@@ -3,11 +3,15 @@ numpy
|
||||
jupyter
|
||||
nbconvert
|
||||
papermill
|
||||
pandas
|
||||
matplotlib
|
||||
tabulate
|
||||
google-cloud-aiplatform
|
||||
google-cloud-storage
|
||||
google-cloud-build
|
||||
google-cloud-storage
|
||||
ratemate
|
||||
GitPython
|
||||
GitPython
|
||||
tqdm
|
||||
fsspec
|
||||
pandas
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
notebooks/official/training/pytorch_gcs_data_training.ipynb
|
||||
notebooks/official/custom/custom_training_tensorboard_profiler.ipynb
|
||||
notebooks/official/custom/custom-tabular-bq-managed-dataset.ipynb
|
||||
notebooks/official/ml_metadata/sdk-metric-parameter-tracking-for-locally-trained-models.ipynb
|
||||
notebooks/official/ml_metadata/sdk-metric-parameter-tracking-for-custom-jobs.ipynb
|
||||
notebooks/official/tabnet/tabnet_vertex_tutorial.ipynb
|
||||
notebooks/official/tabnet/get_started_with_tabnet.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_automl_text.ipynb
|
||||
notebooks/official/pipelines/multicontender_vs_champion_deployment_method.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_automl_images.ipynb
|
||||
notebooks/official/pipelines/rapid_prototyping_bqml_automl.ipynb
|
||||
notebooks/official/pipelines/challenger_vs_blessed_deployment_method.ipynb
|
||||
notebooks/official/matching_engine/sdk_matching_engine_create_stack_overflow_embeddings.ipynb
|
||||
notebooks/official/matching_engine/sdk_matching_engine_for_indexing.ipynb
|
||||
notebooks/official/matching_engine/sdk_matching_engine_create_text_to_image_embeddings.ipynb
|
||||
notebooks/official/explainable_ai/sdk_custom_image_classification_online_explain.ipynb
|
||||
notebooks/official/explainable_ai/xai_image_classification_feature_attributions.ipynb
|
||||
notebooks/official/explainable_ai/sdk_custom_image_classification_batch_explain.ipynb
|
||||
notebooks/official/tabular_workflows/tabnet_on_vertex_pipelines.ipynb
|
||||
notebooks/official/model_registry/get_started_with_model_registry.ipynb
|
||||
notebooks/official/model_registry/bqml_vertexai_model_registry.ipynb
|
||||
notebooks/official/sdk/SDK_Custom_Training_Python_Package_Managed_Text_Dataset_Tensorflow_Serving_Container.ipynb
|
||||
notebooks/official/model_monitoring/batch_prediction_model_monitoring.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_setup.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_custom.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_custom_tf_serving.ipynb
|
||||
notebooks/official/model_monitoring/model_monitoring.ipynb
|
||||
notebooks/official/tensorboard/tensorboard_profiler_custom_training_with_prebuilt_container.ipynb
|
||||
notebooks/official/tensorboard/tensorboard_hyperparameter_tuning_with_hparams.ipynb
|
||||
notebooks/official/tensorboard/tensorboard_profiler_custom_training.ipynb
|
||||
notebooks/official/model_evaluation/custom_tabular_regression_model_evaluation.ipynb
|
||||
notebooks/official/model_evaluation/custom_tabular_classification_model_evaluation.ipynb
|
||||
notebooks/official/model_evaluation/automl_video_classification_model_evaluation.ipynb
|
||||
notebooks/official/experiments/comparing_local_trained_models.ipynb
|
||||
notebooks/official/automl/automl_image_classification_online_online_prediction.ipynb
|
||||
notebooks/official/automl/automl-text-classification.ipynb
|
||||
notebooks/official/automl/sdk_automl_video_object_tracking_batch.ipynb
|
||||
notebooks/official/feature_store/sdk-feature-store-pandas.ipynb
|
||||
notebooks/official/prediction/custom_batch_prediction_feature_filter.ipynb
|
||||
notebooks/official/prediction/pytorch_image_classification_with_prebuilt_serving_containers.ipynb
|
||||
@@ -0,0 +1,80 @@
|
||||
notebooks/official/training/hyperparameter_tuning_tensorflow.ipynb
|
||||
notebooks/official/training/get_started_with_vertex_distributed_training.ipynb
|
||||
notebooks/official/training/hyperparameter_tuning_xgboost.ipynb
|
||||
notebooks/official/training/multi_node_ddp_gloo_vertex_training_with_custom_container.ipynb
|
||||
notebooks/official/training/distributed_hyperparameter_tuning.ipynb
|
||||
notebooks/official/training/pytorch-text-sentiment-classification-custom-train-deploy.ipynb
|
||||
notebooks/official/training/xgboost_data_parallel_training_on_cpu_using_dask.ipynb
|
||||
notebooks/official/training/multi_node_ddp_nccl_vertex_training_with_custom_container.ipynb
|
||||
notebooks/official/bigquery_ml/get_started_with_bqml_training.ipynb
|
||||
notebooks/official/bigquery_ml/bqml-online-prediction.ipynb
|
||||
notebooks/official/custom/custom_training_container_and_model_registry.ipynb
|
||||
notebooks/official/custom/sdk-custom-image-classification-online.ipynb
|
||||
notebooks/official/custom/sdk-custom-image-classification-batch.ipynb
|
||||
notebooks/official/custom/SDK_FBProphet_Forecasting_Online.ipynb
|
||||
notebooks/official/custom/get_started_vertex_training_xgboost.ipynb
|
||||
notebooks/official/custom/get_started_with_vertex_endpoint_and_shared_vm.ipynb
|
||||
notebooks/official/custom/SDK_Custom_Container_Prediction.ipynb
|
||||
notebooks/official/reduction_server/pytorch_distributed_training_reduction_server.ipynb
|
||||
notebooks/official/tabnet/ai-explanations-tabnet-algorithm.ipynb
|
||||
notebooks/official/vizier/get_started_vertex_vizier.ipynb
|
||||
notebooks/official/vizier/gapic-vizier-multi-objective-optimization.ipynb
|
||||
notebooks/official/pipelines/get_started_with_hpt_pipeline_components.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_automl_tabular.ipynb
|
||||
notebooks/official/pipelines/custom_tabular_train_batch_pred_bq_pipeline.ipynb
|
||||
notebooks/official/pipelines/metrics_viz_run_compare_kfp.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_model_upload_predict_evaluate.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_model_train_upload_deploy.ipynb
|
||||
notebooks/official/pipelines/get_started_with_machine_management.ipynb
|
||||
notebooks/official/pipelines/custom_model_training_and_batch_prediction.ipynb
|
||||
notebooks/official/pipelines/control_flow_kfp.ipynb
|
||||
notebooks/official/pipelines/lightweight_functions_component_io_kfp.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_bqml_text.ipynb
|
||||
notebooks/official/pipelines/pipelines_intro_kfp.ipynb
|
||||
notebooks/official/pipelines/automl_tabular_classification_beans.ipynb
|
||||
notebooks/official/pipelines/google_cloud_pipeline_components_dataproc_tabular.ipynb
|
||||
notebooks/official/explainable_ai/sdk_automl_tabular_classification_online_explain.ipynb
|
||||
notebooks/official/explainable_ai/sdk_custom_tabular_regression_online_explain.ipynb
|
||||
notebooks/official/explainable_ai/sdk_automl_tabular_binary_classification_batch_explain.ipynb
|
||||
notebooks/official/explainable_ai/sdk_custom_tabular_regression_online_explain_get_metadata.ipynb
|
||||
notebooks/official/explainable_ai/sdk_custom_tabular_regression_batch_explain.ipynb
|
||||
notebooks/official/tabular_workflows/prophet_on_vertex_pipelines.ipynb
|
||||
notebooks/official/tabular_workflows/wide_and_deep_on_vertex_pipelines.ipynb
|
||||
notebooks/official/sdk/SDK_AutoML_Video_Classification.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_automl.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_automl_image_batch.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_automl_image_online.ipynb
|
||||
notebooks/official/model_monitoring/get_started_with_model_monitoring_xgboost.ipynb
|
||||
notebooks/official/tensorboard/tensorboard_custom_training_with_custom_container.ipynb
|
||||
notebooks/official/tensorboard/tensorboard_custom_training_with_prebuilt_container.ipynb
|
||||
notebooks/official/tensorboard/tensorboard_vertex_ai_pipelines_integration.ipynb
|
||||
notebooks/official/model_evaluation/automl_text_classification_model_evaluation.ipynb
|
||||
notebooks/official/model_evaluation/get_started_with_custom_model_evaluation_import.ipynb
|
||||
notebooks/official/model_evaluation/automl_tabular_classification_model_evaluation.ipynb
|
||||
notebooks/official/model_evaluation/automl_tabular_regression_model_evaluation.ipynb
|
||||
notebooks/official/experiments/get_started_with_vertex_experiments.ipynb
|
||||
notebooks/official/experiments/comparing_pipeline_runs.ipynb
|
||||
notebooks/official/experiments/get_started_with_vertex_experiments_autologging.ipynb
|
||||
notebooks/official/experiments/build_model_experimentation_lineage_with_prebuild_code.ipynb
|
||||
notebooks/official/experiments/delete_outdated_tensorboard_experiments.ipynb
|
||||
notebooks/official/automl/sdk_automl_tabular_regression_batch_bq.ipynb
|
||||
notebooks/official/automl/sdk_automl_text_sentiment_analysis_online.ipynb
|
||||
notebooks/official/automl/sdk_automl_text_entity_extraction_online.ipynb
|
||||
notebooks/official/automl/sdk_automl_forecasting_hierarchical_batch.ipynb
|
||||
notebooks/official/automl/automl_text_entity_extraction_batch_prediction.ipynb
|
||||
notebooks/official/automl/automl_image_classification_batch_prediction.ipynb
|
||||
notebooks/official/automl/automl_text_sentiment_analysis_batch_prediction.ipynb
|
||||
notebooks/official/automl/sdk_automl_tabular_regression_online_bq.ipynb
|
||||
notebooks/official/automl/get_started_automl_training.ipynb
|
||||
notebooks/official/automl/automl-tabular-classification.ipynb
|
||||
notebooks/official/automl/automl_image_object_detection_export_edge.ipynb
|
||||
notebooks/official/automl/sdk_automl_image_object_detection_batch.ipynb
|
||||
notebooks/official/automl/automl_tabular_on_vertex_pipelines.ipynb
|
||||
notebooks/official/automl/sdk_automl_video_classification_batch.ipynb
|
||||
notebooks/official/automl/sdk_automl_video_action_recognition_batch.ipynb
|
||||
notebooks/official/automl/sdk_automl_tabular_forecasting_batch.ipynb
|
||||
notebooks/official/automl/automl_image_object_detection_online_prediction.ipynb
|
||||
notebooks/official/automl/automl_forecasting_bqml_arima_plus_comparison.ipynb
|
||||
notebooks/official/datasets/get_started_bq_datasets.ipynb
|
||||
notebooks/official/datasets/get_started_with_data_labeling.ipynb
|
||||
notebooks/official/feature_store/feature_store_streaming_ingestion_sdk.ipynb
|
||||
@@ -0,0 +1,46 @@
|
||||
# grep PASSED tests.txt | cut -c 10-100 >passed.txt
|
||||
|
||||
import os
|
||||
|
||||
repo_dir = '/home/jupyter/vertex-ai-samples/'
|
||||
repo_dir_len = len(repo_dir)
|
||||
official_dir = repo_dir + 'notebooks/official'
|
||||
|
||||
entries = os.scandir(official_dir)
|
||||
folders = []
|
||||
for entry in entries:
|
||||
if entry.is_dir():
|
||||
folders.append(entry.path)
|
||||
|
||||
# Passing
|
||||
with open('passed.txt', 'r') as pass_file:
|
||||
notebook_names = pass_file.readlines()
|
||||
|
||||
notebooks = []
|
||||
for folder in folders:
|
||||
entries = os.scandir(folder)
|
||||
for entry in entries:
|
||||
for notebook in notebook_names:
|
||||
if entry.name == notebook.rstrip():
|
||||
notebooks.append(entry.path[repo_dir_len:])
|
||||
|
||||
with open('passing_tests.txt', 'w') as f:
|
||||
for notebook in notebooks:
|
||||
f.write(notebook + '\n')
|
||||
|
||||
|
||||
# Failing
|
||||
with open('failed.txt', 'r') as fail_file:
|
||||
notebook_names = fail_file.readlines()
|
||||
|
||||
notebooks = []
|
||||
for folder in folders:
|
||||
entries = os.scandir(folder)
|
||||
for entry in entries:
|
||||
for notebook in notebook_names:
|
||||
if entry.name == notebook.rstrip():
|
||||
notebooks.append(entry.path[repo_dir_len:])
|
||||
|
||||
with open('failing_tests.txt', 'w') as f:
|
||||
for notebook in notebooks:
|
||||
f.write(notebook + '\n')
|
||||
@@ -0,0 +1,33 @@
|
||||
import sys
|
||||
|
||||
from execute_changed_notebooks_helper import (load_results, select_notebook)
|
||||
|
||||
|
||||
def test_load_results():
|
||||
bucket: str = "cloud-build-notebooks-presubmit"
|
||||
bucket_file: str = "build_results"
|
||||
|
||||
accum = load_results(bucket, bucket_file)
|
||||
|
||||
print(accum)
|
||||
|
||||
assert len(accum) > 0
|
||||
|
||||
def test_select_notebook():
|
||||
bucket: str = "cloud-build-notebooks-presubmit"
|
||||
bucket_file: str = "build_results"
|
||||
|
||||
accum = load_results(bucket, bucket_file)
|
||||
|
||||
n_select = 0
|
||||
n_notselect = 0
|
||||
for notebook in accum:
|
||||
if select_notebook(notebook, accum, 50):
|
||||
n_select += 1
|
||||
else:
|
||||
n_notselect += 1
|
||||
|
||||
print(f"SELECTED {n_select}, NOT SELECTED {n_notselect}")
|
||||
|
||||
assert n_select > 0
|
||||
assert n_notselect > 0
|
||||
@@ -0,0 +1,22 @@
|
||||
'''
|
||||
Viewer for the weekly regression testing of the official notebooks
|
||||
|
||||
Cloud Storage location: gs://cloud-build-notebooks-presubmit/build_results/
|
||||
'''
|
||||
import argparse
|
||||
import json
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--file', dest='file',
|
||||
default='build.json', type=str, help='build results file')
|
||||
import json
|
||||
|
||||
with open('build.json', 'r') as f:
|
||||
results = json.load(f)
|
||||
|
||||
for item in results.items():
|
||||
if item[1]['passed']:
|
||||
print(f"{item[0]},PASSED")
|
||||
else:
|
||||
print(f"{item[0]},FAILED")
|
||||
@@ -0,0 +1,23 @@
|
||||
steps:
|
||||
# Fetch full repo for diff purposes
|
||||
- name: gcr.io/cloud-builders/git
|
||||
args: [fetch, --unshallow, --quiet]
|
||||
# Create a virtual environment
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- python3 -m venv workspace/env
|
||||
# Install Python dependencies and run testing script
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 notebooks/notebook_template_review.py --web --title --steps --desc --linkback --notebook-dir=notebooks/official >web.html
|
||||
artifacts:
|
||||
objects:
|
||||
location: gs://${_GCS_ARTIFACTS_BUCKET}/webdoc
|
||||
paths: ['web.html']
|
||||
timeout: 86400s
|
||||
@@ -2,9 +2,9 @@ git+https://github.com/tensorflow/docs
|
||||
ipython
|
||||
jupyter
|
||||
nbconvert
|
||||
black==22.10.0
|
||||
pyupgrade==2.38.4
|
||||
isort==5.10.1
|
||||
flake8==4.0.1
|
||||
nbqa==1.5.3
|
||||
black==23.3.0
|
||||
pyupgrade==3.7.0
|
||||
isort==5.12.0
|
||||
flake8==6.0.0
|
||||
nbqa==1.7.0
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ if [ ${#notebooks[@]} -gt 0 ]; then
|
||||
# python3 -m nbqa black "$notebook" --check
|
||||
# BLACK_RTN=$?
|
||||
echo "Running pyupgrade..."
|
||||
python3 -m nbqa pyupgrade "$notebook"
|
||||
python3 -m nbqa pyupgrade --exit-zero-even-if-changed "$notebook"
|
||||
PYUPGRADE_RTN=$?
|
||||
echo "Running isort..."
|
||||
python3 -m nbqa isort "$notebook" --check
|
||||
@@ -97,7 +97,7 @@ if [ ${#notebooks[@]} -gt 0 ]; then
|
||||
python3 -m nbqa black "$notebook"
|
||||
BLACK_RTN=$?
|
||||
echo "Running pyupgrade..."
|
||||
python3 -m nbqa pyupgrade "$notebook"
|
||||
python3 -m nbqa pyupgrade --exit-zero-even-if-changed "$notebook"
|
||||
PYUPGRADE_RTN=$?
|
||||
echo "Running isort..."
|
||||
python3 -m nbqa isort "$notebook"
|
||||
|
||||
+3
-5
@@ -44,12 +44,10 @@ Finally, run this code block to check for errors. Each step will attempt to
|
||||
automatically fix any issues. If the fixes can't be performed automatically,
|
||||
then you will need to manually address them before submitting your PR.
|
||||
|
||||
Note: For official, only submit one notebook per PR.
|
||||
|
||||
```shell
|
||||
nbqa black "$notebook"
|
||||
nbqa pyupgrade "$notebook"
|
||||
nbqa isort "$notebook"
|
||||
nbqa flake8 "$notebook" --extend-ignore=W391,E501,F821,E402,F404,W503,E203,E722,W293,W291
|
||||
python3 -m tensorflow_docs.tools.nbfmt --remove_outputs "$notebook"
|
||||
docker run -v ${PWD}:/setup/app gcr.io/cloud-devrel-public-resources/notebook_linter:latest your_notebook
|
||||
```
|
||||
|
||||
## Code Reviews
|
||||
|
||||
@@ -8,3 +8,9 @@
|
||||
/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
|
||||
/prediction_featurestore_integration @googleapis/vertex-prediction-team
|
||||
/vertex_vision_model_garden/model_oss/util @weigary
|
||||
/vertex_vision_model_garden/model_oss/diffusers @weigary
|
||||
/vertex_vision_model_garden/model_oss/keras @dstnluong-google
|
||||
/vertex_vision_model_garden/model_oss/transformers @dstnluong-google
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@ download_from_gcs_op = components.load_component_from_url("https://raw.githubuse
|
||||
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/399405402d95f4a011e2d2e967c96f8508ba5688/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/1f5cf6e06409b704064b2086c0a705e4e6b4fcde/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/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
|
||||
|
||||
+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},
|
||||
]
|
||||
+1507
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,3 +1,3 @@
|
||||
torch==1.8.1
|
||||
torch==1.13.1
|
||||
torchvision==0.9.1
|
||||
tensorboard==2.5.0
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
torch==1.8.1
|
||||
torch==1.13.1
|
||||
torchvision==0.9.1
|
||||
tensorboard==2.5.0
|
||||
+1
-11
@@ -31,17 +31,7 @@
|
||||
"source": [
|
||||
"# Deploying a PyTorch Text Classification Model on [Vertex AI](https://cloud.google.com/vertex-ai)\n",
|
||||
"\n",
|
||||
"**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).\n",
|
||||
"\n",
|
||||
"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.\n",
|
||||
"\n",
|
||||
"**Kindly drop us a note before you run any scale tests.**\n",
|
||||
"\n",
|
||||
"**Do not hesitate to contact vertexai-prediction-preview-feedback@google.com if you have any questions or run into any issues.**\n",
|
||||
"\n",
|
||||
"The usage of the product is free during the Experimental release period: you will still incur charges for other GCP products usage, such as storage.\n",
|
||||
"\n",
|
||||
"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."
|
||||
"**Kindly reach out to Vertex AI before you run any scale tests or you have any questions.**\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
[MASTER]
|
||||
|
||||
generated-members=get_concrete_function,cv2.*
|
||||
ignored-modules=tensorflow,google.cloud
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# Dockerfile for Diffuser Serving.
|
||||
#
|
||||
# To build:
|
||||
# docker build -f model_oss/diffusers/dockerfile/serve.Dockerfile . -t ${YOUR_IMAGE_TAG}
|
||||
#
|
||||
# To push to gcr:
|
||||
# docker tag ${YOUR_IMAGE_TAG} gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
# docker push gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
|
||||
FROM pytorch/torchserve:0.7.0-gpu
|
||||
|
||||
USER root
|
||||
|
||||
ENV infer_port=7080
|
||||
ENV mng_port=7081
|
||||
ENV model_name="diffusers_serving"
|
||||
ENV PATH="/home/model-server/:${PATH}"
|
||||
|
||||
# Install libraries.
|
||||
ENV PIP_ROOT_USER_ACTION=ignore
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN pip install torch==1.13.1
|
||||
RUN pip install torchvision==0.14.1
|
||||
RUN pip install transformers==4.27.4
|
||||
RUN pip install datasets==2.9.0
|
||||
RUN pip install accelerate==0.17.0
|
||||
RUN pip install triton==2.0.0.dev20221120
|
||||
RUN pip install xformers==0.0.16
|
||||
RUN pip install google-cloud-storage==2.7.0
|
||||
RUN pip install imageio[ffmpeg]==2.31.0
|
||||
RUN pip install absl-py==1.4.0
|
||||
|
||||
# Copy LICENSE file
|
||||
RUN apt-get update && apt-get install wget
|
||||
RUN wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/LICENSE
|
||||
|
||||
# Install diffusers from main branch source code with a pinned commit.
|
||||
RUN git clone --depth 1 --branch v0.18.1 https://github.com/huggingface/diffusers.git
|
||||
WORKDIR diffusers
|
||||
RUN pip install -e .
|
||||
|
||||
# Copy model artifacts.
|
||||
COPY model_oss/diffusers/handler.py /home/model-server/handler.py
|
||||
COPY model_oss/util/ /home/model-server/util/
|
||||
ENV PYTHONPATH /home/model-server/
|
||||
|
||||
# Create torchserve configuration file.
|
||||
RUN echo \
|
||||
"default_response_timeout=1800\n" \
|
||||
"service_envelope=json\n" \
|
||||
"inference_address=http://0.0.0.0:${infer_port}\n" \
|
||||
"management_address=http://0.0.0.0:${mng_port}" >> /home/model-server/config.properties
|
||||
|
||||
# Expose ports.
|
||||
EXPOSE ${infer_port}
|
||||
EXPOSE ${mng_port}
|
||||
|
||||
# Archive model artifacts and dependencies.
|
||||
# Do not set --model-file and --serialized-file because model and checkpoint
|
||||
# will be dynamically loaded in handler.py.
|
||||
RUN torch-model-archiver \
|
||||
--model-name=${model_name} \
|
||||
--version=1.0 \
|
||||
--handler=/home/model-server/handler.py \
|
||||
--runtime=python3 \
|
||||
--export-path=/home/model-server/model-store \
|
||||
--archive-format=default \
|
||||
--force
|
||||
|
||||
# Run Torchserve HTTP serve to respond to prediction requests.
|
||||
CMD ["torchserve", "--start", \
|
||||
"--ts-config", "/home/model-server/config.properties", \
|
||||
"--models", "${model_name}=${model_name}.mar", \
|
||||
"--model-store", "/home/model-server/model-store"]
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# Dockerfile for Diffuser Training.
|
||||
#
|
||||
# To build:
|
||||
# docker build -f model_oss/diffusers/dockerfile/train.Dockerfile . -t ${YOUR_IMAGE_TAG}
|
||||
#
|
||||
# To push to gcr:
|
||||
# docker tag ${YOUR_IMAGE_TAG} gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
# docker push gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
|
||||
# Base on pytorch-cuda image.
|
||||
FROM pytorch/pytorch:1.13.0-cuda11.6-cudnn8-runtime
|
||||
|
||||
# Install tools.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
wget \
|
||||
git \
|
||||
vim
|
||||
|
||||
# Copy license.
|
||||
RUN wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/LICENSE
|
||||
|
||||
# Install libraries.
|
||||
RUN pip install torchvision==0.14.1
|
||||
RUN pip install transformers==4.26.1
|
||||
RUN pip install datasets==2.9.0
|
||||
RUN pip install accelerate==0.17.0
|
||||
RUN pip install triton==2.0.0.dev20221120
|
||||
RUN pip install xformers==0.0.16
|
||||
RUN pip install Jinja2==3.1.2
|
||||
RUN pip install ftfy==6.1.1
|
||||
RUN pip install cloudml-hypertune==0.1.0.dev6
|
||||
RUN pip install tensorboard==2.12.0
|
||||
|
||||
# Install diffusers from main branch source code with a pinned commit.
|
||||
RUN git clone --depth 1 --branch v0.18.1 https://github.com/huggingface/diffusers.git
|
||||
WORKDIR diffusers
|
||||
RUN pip install -e .
|
||||
|
||||
# Switch to diffusers examples folder.
|
||||
WORKDIR examples
|
||||
|
||||
# Config accelerate.
|
||||
COPY model_oss/diffusers/train.sh train.sh
|
||||
|
||||
# Generate accelerate config at the beginning of docker run.
|
||||
ENTRYPOINT ["/bin/bash", "train.sh"]
|
||||
@@ -0,0 +1,256 @@
|
||||
"""Custom handler for huggingface/diffusers models."""
|
||||
|
||||
# pylint: disable=g-importing-member
|
||||
# pylint: disable=logging-fstring-interpolation
|
||||
|
||||
import base64
|
||||
import io
|
||||
import logging
|
||||
import os
|
||||
from typing import Any, List, Sequence, Tuple
|
||||
|
||||
from diffusers import ControlNetModel
|
||||
from diffusers import DiffusionPipeline
|
||||
from diffusers import DPMSolverMultistepScheduler
|
||||
from diffusers import EulerAncestralDiscreteScheduler
|
||||
from diffusers import StableDiffusionControlNetPipeline
|
||||
from diffusers import StableDiffusionImg2ImgPipeline
|
||||
from diffusers import StableDiffusionInpaintPipeline
|
||||
from diffusers import StableDiffusionInstructPix2PixPipeline
|
||||
from diffusers import StableDiffusionPipeline
|
||||
from diffusers import StableDiffusionUpscalePipeline
|
||||
from diffusers import TextToVideoZeroPipeline
|
||||
from diffusers import UniPCMultistepScheduler
|
||||
import imageio
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
import torch
|
||||
from ts.torch_handler.base_handler import BaseHandler
|
||||
|
||||
from util import constants
|
||||
from util import fileutils
|
||||
from util import image_format_converter
|
||||
from video_util import video_format_converter
|
||||
|
||||
STABLE_DIFFUSION_MODEL = "runwayml/stable-diffusion-v1-5"
|
||||
|
||||
# Tasks
|
||||
TEXT_TO_IMAGE = "text-to-image"
|
||||
IMAGE_TO_IMAGE = "image-to-image"
|
||||
IMAGE_INPAINTING = "image-inpainting"
|
||||
INSTRUCT_PIX2PIX = "instruct-pix2pix"
|
||||
CONTROLNET = "controlnet"
|
||||
CONDITIONED_SUPER_RES = "conditioned-super-res"
|
||||
TEXT_TO_VIDEO_ZERO_SHOT = "text-to-video-zero-shot"
|
||||
TEXT_TO_VIDEO = "text-to-video"
|
||||
|
||||
|
||||
def frames_to_video_bytes(frames: Sequence[np.ndarray], fps: int) -> bytes:
|
||||
images = [Image.fromarray(array) for array in frames]
|
||||
io_obj = io.BytesIO()
|
||||
imageio.mimsave(io_obj, images, format=".mp4", fps=fps)
|
||||
return io_obj.getvalue()
|
||||
|
||||
|
||||
class DiffusersHandler(BaseHandler):
|
||||
"""Custom handler for TIMM models."""
|
||||
|
||||
def initialize(self, context: Any):
|
||||
"""Custom initialize."""
|
||||
|
||||
properties = context.system_properties
|
||||
self.map_location = (
|
||||
"cuda"
|
||||
if torch.cuda.is_available() and properties.get("gpu_id") is not None
|
||||
else "cpu"
|
||||
)
|
||||
self.device = torch.device(
|
||||
self.map_location + ":" + str(properties.get("gpu_id"))
|
||||
if torch.cuda.is_available() and properties.get("gpu_id") is not None
|
||||
else self.map_location
|
||||
)
|
||||
self.manifest = context.manifest
|
||||
|
||||
self.model_id = os.environ["MODEL_ID"]
|
||||
if self.model_id.startswith(constants.GCS_URI_PREFIX):
|
||||
gcs_path = self.model_id[len(constants.GCS_URI_PREFIX) :]
|
||||
local_model_dir = os.path.join(constants.LOCAL_MODEL_DIR, gcs_path)
|
||||
logging.info(f"Download {self.model_id} to {local_model_dir}")
|
||||
fileutils.download_gcs_dir_to_local(self.model_id, local_model_dir)
|
||||
self.model_id = local_model_dir
|
||||
|
||||
self.task = os.environ.get("TASK", TEXT_TO_IMAGE)
|
||||
logging.info(f"Using task:{self.task}, model:{self.model_id}")
|
||||
|
||||
if self.task == TEXT_TO_IMAGE:
|
||||
pipeline = StableDiffusionPipeline.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16
|
||||
)
|
||||
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
# Reduce memory footprint.
|
||||
pipeline.enable_attention_slicing()
|
||||
elif self.task == IMAGE_TO_IMAGE:
|
||||
pipeline = StableDiffusionImg2ImgPipeline.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16
|
||||
)
|
||||
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
# Reduce memory footprint.
|
||||
pipeline.enable_attention_slicing()
|
||||
elif self.task == IMAGE_INPAINTING:
|
||||
pipeline = StableDiffusionInpaintPipeline.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16
|
||||
)
|
||||
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
# Reduce memory footprint.
|
||||
pipeline.enable_attention_slicing()
|
||||
elif self.task == INSTRUCT_PIX2PIX:
|
||||
pipeline = StableDiffusionInstructPix2PixPipeline.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16
|
||||
)
|
||||
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
# Reduce memory footprint.
|
||||
pipeline.enable_attention_slicing()
|
||||
elif self.task == CONTROLNET:
|
||||
controlnet = ControlNetModel.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16
|
||||
)
|
||||
pipeline = StableDiffusionControlNetPipeline.from_pretrained(
|
||||
STABLE_DIFFUSION_MODEL,
|
||||
controlnet=controlnet,
|
||||
torch_dtype=torch.float16,
|
||||
)
|
||||
pipeline.scheduler = UniPCMultistepScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
pipeline.enable_xformers_memory_efficient_attention()
|
||||
pipeline.enable_model_cpu_offload()
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
# Reduce memory footprint.
|
||||
pipeline.enable_attention_slicing()
|
||||
elif self.task == CONDITIONED_SUPER_RES:
|
||||
pipeline = StableDiffusionUpscalePipeline.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16
|
||||
)
|
||||
pipeline.scheduler = EulerAncestralDiscreteScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
# This is necessary to 4x upscale >=256x256 input images with V100.
|
||||
logging.info("Enable xformers memory efficient attention for inference.")
|
||||
pipeline.enable_xformers_memory_efficient_attention()
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
# Reduce memory footprint.
|
||||
pipeline.enable_attention_slicing()
|
||||
elif self.task == TEXT_TO_VIDEO_ZERO_SHOT:
|
||||
pipeline = TextToVideoZeroPipeline.from_pretrained(
|
||||
STABLE_DIFFUSION_MODEL, torch_dtype=torch.float16
|
||||
)
|
||||
# Memory optimization.
|
||||
pipeline.enable_xformers_memory_efficient_attention()
|
||||
pipeline.enable_model_cpu_offload()
|
||||
pipeline = pipeline.to(self.map_location)
|
||||
elif self.task == TEXT_TO_VIDEO:
|
||||
pipeline = DiffusionPipeline.from_pretrained(
|
||||
self.model_id, torch_dtype=torch.float16, variant="fp16"
|
||||
)
|
||||
pipeline.enable_model_cpu_offload()
|
||||
# Memory optimization.
|
||||
pipeline.enable_vae_slicing()
|
||||
pipeline.scheduler = DPMSolverMultistepScheduler.from_config(
|
||||
pipeline.scheduler.config
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Invalid TASK: {self.task}")
|
||||
|
||||
self.pipeline = pipeline
|
||||
self.initialized = True
|
||||
logging.info("Handler initialization done.")
|
||||
|
||||
def preprocess(self, data: Any) -> Tuple[Any, Any, Any]:
|
||||
"""Preprocess input data."""
|
||||
prompts = [item["prompt"] for item in data]
|
||||
images = None
|
||||
mask_images = None
|
||||
|
||||
if "image" in data[0]:
|
||||
images = [
|
||||
image_format_converter.base64_to_image(item["image"]) for item in data
|
||||
]
|
||||
if "mask_image" in data[0]:
|
||||
mask_images = [
|
||||
image_format_converter.base64_to_image(item["mask_image"])
|
||||
for item in data
|
||||
]
|
||||
return prompts, images, mask_images
|
||||
|
||||
def inference(self, data: Any, *args, **kwargs) -> List[Image.Image]:
|
||||
"""Run the inference."""
|
||||
prompts, images, mask_images = data
|
||||
if self.task == TEXT_TO_IMAGE:
|
||||
predicted_images = self.pipeline(prompt=prompts).images
|
||||
elif self.task == IMAGE_TO_IMAGE:
|
||||
predicted_images = self.pipeline(prompt=prompts, image=images).images
|
||||
elif self.task == IMAGE_INPAINTING:
|
||||
predicted_images = self.pipeline(
|
||||
prompt=prompts, image=images, mask_image=mask_images
|
||||
).images
|
||||
elif self.task == INSTRUCT_PIX2PIX:
|
||||
predicted_images = self.pipeline(prompt=prompts, image=images).images
|
||||
elif self.task == CONTROLNET:
|
||||
predicted_images = self.pipeline(
|
||||
prompt=prompts, image=images, num_inference_steps=20
|
||||
).images
|
||||
elif self.task == CONDITIONED_SUPER_RES:
|
||||
predicted_images = self.pipeline(
|
||||
prompt=prompts, image=images, num_inference_steps=20
|
||||
).images
|
||||
elif self.task == TEXT_TO_VIDEO_ZERO_SHOT:
|
||||
# For each given prompt, generate a short video.
|
||||
# The pipeline doesn't support multiple prompts in one run yet.
|
||||
videos = []
|
||||
for prompt in prompts:
|
||||
numpy_arrays = self.pipeline(prompt=prompt).images
|
||||
numpy_arrays = [(i * 255).astype("uint8") for i in numpy_arrays]
|
||||
videos.append(
|
||||
frames_to_video_bytes(numpy_arrays, fps=4)
|
||||
)
|
||||
return videos
|
||||
elif self.task == TEXT_TO_VIDEO:
|
||||
predicted_images = np.asarray(self.pipeline(prompt=prompts).frames)
|
||||
# For multiple prompts, the model concatenates video frames, i.e. the
|
||||
# output shape is (num_frames, height, width * len(prompts), channels).
|
||||
# Therefore we need to split the output into different videos.
|
||||
predicted_images = np.array_split(predicted_images, len(prompts), axis=2)
|
||||
videos = [
|
||||
frames_to_video_bytes(images, fps=8)
|
||||
for images in predicted_images
|
||||
]
|
||||
return videos
|
||||
else:
|
||||
raise ValueError(f"Invalid TASK: {self.task}")
|
||||
return predicted_images
|
||||
|
||||
def postprocess(self, data: Any) -> List[str]:
|
||||
"""Convert the images to base64 string."""
|
||||
outputs = []
|
||||
for prediction in data:
|
||||
if isinstance(prediction, bytes):
|
||||
# This is the video bytes.
|
||||
outputs.append(base64.b64encode(prediction).decode("utf-8"))
|
||||
else:
|
||||
outputs.append(image_format_converter.image_to_base64(prediction))
|
||||
return outputs
|
||||
|
||||
|
||||
# pylint: enable=logging-fstring-interpolation
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Setup accelerate config before running trainer.
|
||||
python -c "from accelerate.utils import write_basic_config; write_basic_config(mixed_precision='fp16')"
|
||||
|
||||
accelerate launch "$@"
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
# Dockerfile for basic serving dockers with Keras.
|
||||
#
|
||||
# To build:
|
||||
# docker build -f model_oss/keras/dockerfile/serve.Dockerfile . -t ${YOUR_IMAGE_TAG}
|
||||
#
|
||||
# To push to gcr:
|
||||
# docker tag ${YOUR_IMAGE_TAG} gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
# docker push gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
|
||||
FROM tensorflow/tensorflow:2.12.0-gpu
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# This is added to fix docker build error related to Nvidia key update.
|
||||
RUN rm -f /etc/apt/sources.list.d/cuda.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
|
||||
# Install basic libs.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
curl \
|
||||
wget \
|
||||
sudo \
|
||||
gnupg \
|
||||
libsm6 \
|
||||
libxext6 \
|
||||
libxrender-dev \
|
||||
lsb-release \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
git \
|
||||
vim \
|
||||
screen \
|
||||
libtcmalloc-minimal4
|
||||
|
||||
|
||||
# Install google cloud SDK.
|
||||
RUN wget -q https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-359.0.0-linux-x86_64.tar.gz
|
||||
RUN tar xzf google-cloud-sdk-359.0.0-linux-x86_64.tar.gz
|
||||
RUN ./google-cloud-sdk/install.sh -q
|
||||
# Make sure gsutil will use the default service account.
|
||||
RUN echo '[GoogleCompute]\nservice_account = default' > /etc/boto.cfg
|
||||
|
||||
|
||||
# Install required libs.
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install cloud-tpu-client==0.10
|
||||
RUN pip install pyyaml==5.4.1
|
||||
RUN pip install fsspec==2021.10.1
|
||||
RUN pip install gcsfs==2021.10.1
|
||||
RUN pip install tensorflow-text==2.11.0
|
||||
RUN pip install pyglove==0.1.0
|
||||
RUN pip install cloudml-hypertune==0.1.0.dev6
|
||||
RUN pip install pylint==2.17.2
|
||||
RUN pip install keras-cv==0.4.0
|
||||
RUN pip install tensorflow-datasets==4.8.3
|
||||
RUN pip install protobuf==3.20.3
|
||||
RUN pip install Pillow==9.5.0
|
||||
RUN pip install flask==2.3.2
|
||||
RUN pip install waitress==2.1.2
|
||||
|
||||
# Installs Reduction Server NCCL plugin.
|
||||
RUN echo "deb https://packages.cloud.google.com/apt google-fast-socket main" | tee /etc/apt/sources.list.d/google-fast-socket.list \
|
||||
&& curl -s -L https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - \
|
||||
&& apt update && apt install -y google-reduction-server
|
||||
|
||||
# Downloading gcloud package
|
||||
RUN curl https://dl.google.com/dl/cloudsdk/release/google-cloud-sdk.tar.gz > /tmp/google-cloud-sdk.tar.gz
|
||||
|
||||
# Installing the package
|
||||
RUN mkdir -p /usr/local/gcloud \
|
||||
&& tar -C /usr/local/gcloud -xvf /tmp/google-cloud-sdk.tar.gz \
|
||||
&& /usr/local/gcloud/google-cloud-sdk/install.sh
|
||||
|
||||
RUN wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/LICENSE
|
||||
|
||||
# Adding the package path to local
|
||||
ENV PATH $PATH:/usr/local/gcloud/google-cloud-sdk/bin
|
||||
|
||||
|
||||
ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
|
||||
|
||||
# Lower the memory fragmentation, and speed up the training.
|
||||
# https://github.com/tensorflow/tensorflow/issues/44176#issuecomment-783768033
|
||||
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc_minimal.so.4
|
||||
|
||||
# Enable userspace DNS cache
|
||||
ENV GCS_RESOLVE_REFRESH_SECS=60
|
||||
ENV GCS_REQUEST_CONNECTION_TIMEOUT_SECS=300
|
||||
ENV GCS_METADATA_REQUEST_TIMEOUT_SECS=300
|
||||
ENV GCS_READ_REQUEST_TIMEOUT_SECS=300
|
||||
ENV GCS_WRITE_REQUEST_TIMEOUT_SECS=600
|
||||
# Each opened GCS file takes GCS_READ_CACHE_BLOCK_SIZE_MB of RAM, reduce the
|
||||
# value from the default 64MB to 8MB to decrease memory footprint.
|
||||
ENV GCS_READ_CACHE_BLOCK_SIZE_MB=8
|
||||
|
||||
EXPOSE 8501
|
||||
|
||||
WORKDIR /usr/local/lib/python3.8/dist-packages/official/vision
|
||||
|
||||
COPY model_oss/keras /automl_vision/keras
|
||||
COPY model_oss/util /automl_vision/util
|
||||
|
||||
WORKDIR /automl_vision
|
||||
|
||||
ENV PYTHONPATH "${PYTHONPATH}:/automl_vision/util"
|
||||
ENV MODEL_PATH ""
|
||||
ENV IMAGE_WIDTH "512"
|
||||
ENV IMAGE_HEIGHT "512"
|
||||
|
||||
COPY model_oss/keras/serve.py ./app.py
|
||||
|
||||
# Run pylint to validate code.
|
||||
COPY .pylintrc /automl_vision/.pylintrc
|
||||
RUN find . -type f -name "*.py" | xargs pylint --rcfile=./.pylintrc --errors-only
|
||||
|
||||
ENTRYPOINT ["flask","run"]
|
||||
CMD ["--host=0.0.0.0", "--port=8501"]
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
# Dockerfile for basic training dockers with Keras.
|
||||
#
|
||||
# To build:
|
||||
# docker build -f model_oss/keras/dockerfile/train.Dockerfile . -t ${YOUR_IMAGE_TAG}
|
||||
#
|
||||
# To push to gcr:
|
||||
# docker tag ${YOUR_IMAGE_TAG} gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
# docker push gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
|
||||
FROM tensorflow/tensorflow:2.12.0-gpu
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# This is added to fix docker build error related to Nvidia key update.
|
||||
RUN rm -f /etc/apt/sources.list.d/cuda.list
|
||||
RUN curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
|
||||
|
||||
# Install basic libs.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
cmake \
|
||||
curl \
|
||||
wget \
|
||||
sudo \
|
||||
gnupg \
|
||||
libsm6 \
|
||||
libxext6 \
|
||||
libxrender-dev \
|
||||
lsb-release \
|
||||
ca-certificates \
|
||||
build-essential \
|
||||
git \
|
||||
vim \
|
||||
screen \
|
||||
libtcmalloc-minimal4
|
||||
|
||||
|
||||
# Install google cloud SDK.
|
||||
RUN wget -q https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-359.0.0-linux-x86_64.tar.gz
|
||||
RUN tar xzf google-cloud-sdk-359.0.0-linux-x86_64.tar.gz
|
||||
RUN ./google-cloud-sdk/install.sh -q
|
||||
# Make sure gsutil will use the default service account.
|
||||
RUN echo '[GoogleCompute]\nservice_account = default' > /etc/boto.cfg
|
||||
|
||||
|
||||
# Install required libs.
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install cloud-tpu-client==0.10
|
||||
RUN pip install pyyaml==5.4.1
|
||||
RUN pip install fsspec==2021.10.1
|
||||
RUN pip install gcsfs==2021.10.1
|
||||
RUN pip install tensorflow-text==2.11.0
|
||||
RUN pip install pyglove==0.1.0
|
||||
RUN pip install cloudml-hypertune==0.1.0.dev6
|
||||
RUN pip install pylint==2.17.2
|
||||
RUN pip install keras-cv==0.4.0
|
||||
RUN pip install tensorflow-datasets==4.8.3
|
||||
RUN pip install tensorflow-estimator==2.12.0
|
||||
RUN pip install tensorflow-gcs-config==2.12.0
|
||||
RUN pip install tensorflow-hub==0.13.0
|
||||
RUN pip install tensorflow-io-gcs-filesystem==0.32.0
|
||||
RUN pip install tensorflow-metadata==1.13.1
|
||||
RUN pip install tensorflow-probability==0.19.0
|
||||
RUN pip install tensorboard==2.12.2
|
||||
RUN pip install tensorboard-data-server==0.7.0
|
||||
RUN pip install tensorboard-plugin-wit==1.8.1
|
||||
RUN pip install protobuf==3.20.3
|
||||
RUN pip install pandas==1.5.3
|
||||
RUN pip install pandas-datareader==0.10.0
|
||||
RUN pip install pandas-gbq==0.17.9
|
||||
RUN pip install pycocotools==2.0.6
|
||||
|
||||
# Installs Reduction Server NCCL plugin.
|
||||
RUN echo "deb https://packages.cloud.google.com/apt google-fast-socket main" | tee /etc/apt/sources.list.d/google-fast-socket.list \
|
||||
&& curl -s -L https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add - \
|
||||
&& apt update && apt install -y google-reduction-server
|
||||
|
||||
RUN wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/LICENSE
|
||||
|
||||
ENV PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
|
||||
|
||||
# Lower the memory fragmentation, and speed up the training.
|
||||
# https://github.com/tensorflow/tensorflow/issues/44176#issuecomment-783768033
|
||||
ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc_minimal.so.4
|
||||
|
||||
# Enable userspace DNS cache
|
||||
ENV GCS_RESOLVE_REFRESH_SECS=60
|
||||
ENV GCS_REQUEST_CONNECTION_TIMEOUT_SECS=300
|
||||
ENV GCS_METADATA_REQUEST_TIMEOUT_SECS=300
|
||||
ENV GCS_READ_REQUEST_TIMEOUT_SECS=300
|
||||
ENV GCS_WRITE_REQUEST_TIMEOUT_SECS=600
|
||||
# Each opened GCS file takes GCS_READ_CACHE_BLOCK_SIZE_MB of RAM, reduce the
|
||||
# value from the default 64MB to 8MB to decrease memory footprint.
|
||||
ENV GCS_READ_CACHE_BLOCK_SIZE_MB=8
|
||||
|
||||
WORKDIR /usr/local/lib/python3.8/dist-packages/official/vision
|
||||
|
||||
COPY model_oss/keras /automl_vision/keras
|
||||
COPY model_oss/util /automl_vision/util
|
||||
|
||||
WORKDIR /automl_vision
|
||||
|
||||
# Keras stable diffusion training codes set width and height as RESOLUTION.
|
||||
ENV RESOLUTION "512"
|
||||
|
||||
ENV PYTHONPATH "${PYTHONPATH}:/automl_vision/util"
|
||||
|
||||
# Run pylint to validate code.
|
||||
COPY .pylintrc /automl_vision/.pylintrc
|
||||
RUN find . -type f -name "*.py" | xargs pylint --rcfile=./.pylintrc --errors-only
|
||||
|
||||
ENTRYPOINT ["python3","keras/train.py"]
|
||||
@@ -0,0 +1,184 @@
|
||||
r"""Servers Keras Stable Diffusion models.
|
||||
|
||||
python serve.py --model_path=<model path in gcs>
|
||||
|
||||
curl -d \
|
||||
'{"prompt":"Hello Kitty"}' \
|
||||
-H "Content-Type: application/json" \
|
||||
-X POST http://localhost:8501/predict
|
||||
"""
|
||||
|
||||
import base64
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
from typing import List, Tuple
|
||||
|
||||
from absl import app
|
||||
# The docker builds could not find flask and waitress.
|
||||
# pylint: disable=import-error
|
||||
from flask import Flask
|
||||
from flask import request
|
||||
from flask import Response
|
||||
import keras_cv
|
||||
from PIL import Image
|
||||
from waitress import serve
|
||||
|
||||
from util import constants
|
||||
from util import fileutils
|
||||
|
||||
|
||||
flask_app = Flask(__name__)
|
||||
|
||||
stable_diffusion_model = None
|
||||
|
||||
|
||||
model_path = os.environ.get('MODEL_PATH', '')
|
||||
if model_path.startswith(constants.GCS_URI_PREFIX):
|
||||
print('Downloading models from gcs to local.')
|
||||
os.makedirs(constants.LOCAL_MODEL_DIR, exist_ok=True)
|
||||
fileutils.download_gcs_dir_to_local(
|
||||
os.path.dirname(model_path), constants.LOCAL_MODEL_DIR
|
||||
)
|
||||
model_path = os.path.join(
|
||||
constants.LOCAL_MODEL_DIR, os.path.basename(model_path)
|
||||
)
|
||||
|
||||
image_width = int(os.environ.get('IMAGE_WIDTH', 512))
|
||||
image_height = int(os.environ.get('IMAGE_HEIGHT', 512))
|
||||
|
||||
print('image_width=', image_width, 'image_height=', image_height)
|
||||
print('Create Keras stable diffusion models.')
|
||||
stable_diffusion_model = keras_cv.models.StableDiffusion(
|
||||
img_width=image_width,
|
||||
img_height=image_height,
|
||||
jit_compile=True,
|
||||
)
|
||||
|
||||
if model_path:
|
||||
# We just reload the weights of the fine-tuned diffusion model.
|
||||
print('Initialize finetuned models from: ', model_path)
|
||||
stable_diffusion_model.diffusion_model.load_weights(model_path)
|
||||
|
||||
|
||||
def error(message: str) -> str:
|
||||
"""Returns a JSON representing an error response."""
|
||||
return json.dumps({
|
||||
'success': False,
|
||||
'error': message,
|
||||
})
|
||||
|
||||
|
||||
def check_key_in_json(content: str, keys: List[str]) -> str:
|
||||
for key in keys:
|
||||
if key not in content:
|
||||
return error('No {} in request {}.'.format(key, content))
|
||||
return None
|
||||
|
||||
|
||||
def validate_json_key(json_key_string: str) -> Tuple[str, bool]:
|
||||
try:
|
||||
json_key = json.loads(json_key_string)
|
||||
except (ValueError, TypeError):
|
||||
return (error('Invalid key found in request'), False)
|
||||
return (json_key, True)
|
||||
|
||||
|
||||
# The health check route is required for docker deployment in google cloud.
|
||||
@flask_app.route('/ping')
|
||||
def ping() -> Response:
|
||||
"""Health checks."""
|
||||
return Response(status=200)
|
||||
|
||||
|
||||
# The return should be `Response` for docker deployment in google cloud.
|
||||
@flask_app.route('/predict', methods=['GET', 'POST'])
|
||||
def predict_model() -> Response:
|
||||
"""Predictions."""
|
||||
if request.method == 'POST':
|
||||
contents = request.get_json(force=True)
|
||||
|
||||
print('The input contents are:', contents)
|
||||
batch_size = 1
|
||||
num_steps = 25
|
||||
seed = 1234
|
||||
if 'parameters' in contents:
|
||||
parameters = contents['parameters']
|
||||
if 'batch_size' in parameters:
|
||||
batch_size = int(parameters['batch_size'])
|
||||
if 'num_steps' in parameters:
|
||||
num_steps = int(parameters['num_steps'])
|
||||
if 'seed' in parameters:
|
||||
seed = int(parameters['seed'])
|
||||
print('batch_size=', batch_size, 'num_steps=', num_steps, 'seed=', seed)
|
||||
if batch_size < 1:
|
||||
return Response(
|
||||
response=error('The batch size must be a positive integar.'),
|
||||
status=200,
|
||||
mimetype='text/plain',
|
||||
)
|
||||
if num_steps < 1:
|
||||
return Response(
|
||||
response=error('The num steps must be a positive integar.'),
|
||||
status=200,
|
||||
mimetype='text/plain',
|
||||
)
|
||||
predictions = []
|
||||
for content in contents['instances']:
|
||||
print('Processing:', content)
|
||||
prompt = content['prompt']
|
||||
generated_image_array = stable_diffusion_model.text_to_image(
|
||||
prompt=prompt,
|
||||
batch_size=batch_size,
|
||||
num_steps=num_steps,
|
||||
seed=seed,
|
||||
)
|
||||
|
||||
generated_image_bytes_array = []
|
||||
for i in range(batch_size):
|
||||
generated_image = Image.fromarray(generated_image_array[i])
|
||||
# Converts the image to a base64-encoded string.
|
||||
buffered_image = io.BytesIO()
|
||||
generated_image.save(buffered_image, format='JPEG')
|
||||
generated_image_bytes = base64.b64encode(
|
||||
buffered_image.getvalue()
|
||||
).decode('utf-8')
|
||||
generated_image_bytes_array.append(generated_image_bytes)
|
||||
prediction = {
|
||||
'prompt': prompt,
|
||||
'predicted_image': generated_image_bytes_array,
|
||||
}
|
||||
predictions.append(prediction)
|
||||
|
||||
return Response(
|
||||
response=json.dumps({
|
||||
'success': True,
|
||||
'predictions': predictions,
|
||||
}),
|
||||
status=200,
|
||||
mimetype='text/plain',
|
||||
)
|
||||
else:
|
||||
return Response(
|
||||
response=json.dumps({
|
||||
'success': True,
|
||||
'isalive': stable_diffusion_model is not None,
|
||||
}),
|
||||
status=200,
|
||||
mimetype='text/plain',
|
||||
)
|
||||
|
||||
|
||||
def serve_main(unused_argv):
|
||||
"""The main function to serve Keras models."""
|
||||
del unused_argv
|
||||
# This is used when running locally only. When deploying to Google App
|
||||
# Engine, a webserver process such as Gunicorn will serve the app.
|
||||
# # Debug deployment.
|
||||
# flask_app.run(host='0.0.0.0', port=8501, debug=True)
|
||||
# Prod deployment.
|
||||
serve(flask_app, host='0.0.0.0', port=8501)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(serve_main)
|
||||
@@ -0,0 +1,363 @@
|
||||
"""Train Keras Stable Diffusion.
|
||||
|
||||
Most the codes below are from
|
||||
https://keras.io/examples/generative/finetune_stable_diffusion/.
|
||||
"""
|
||||
import os
|
||||
|
||||
from absl import app
|
||||
from absl import flags
|
||||
from absl import logging
|
||||
import keras_cv
|
||||
# pylint: disable=g-importing-member
|
||||
from keras_cv.models.stable_diffusion.clip_tokenizer import SimpleTokenizer
|
||||
from keras_cv.models.stable_diffusion.diffusion_model import DiffusionModel
|
||||
from keras_cv.models.stable_diffusion.image_encoder import ImageEncoder
|
||||
from keras_cv.models.stable_diffusion.noise_scheduler import NoiseScheduler
|
||||
from keras_cv.models.stable_diffusion.text_encoder import TextEncoder
|
||||
import numpy as np
|
||||
# The docker builds could not find pandas.
|
||||
# pylint: disable=import-error
|
||||
import pandas as pd
|
||||
import tensorflow as tf
|
||||
from tensorflow import keras
|
||||
import tensorflow.experimental.numpy as tnp
|
||||
|
||||
from util import constants
|
||||
from util import fileutils
|
||||
|
||||
_INPUT_CSV_PATH = flags.DEFINE_string(
|
||||
'input_csv_path',
|
||||
None,
|
||||
'The input csv path.',
|
||||
required=True,
|
||||
)
|
||||
|
||||
_USE_MP = flags.DEFINE_bool(
|
||||
'use_mp',
|
||||
True,
|
||||
'Enable mixed-precision training if the underlying GPU has tensor cores.',
|
||||
)
|
||||
|
||||
_EPOCHS = flags.DEFINE_integer('epochs', 1, 'The number of epochs.')
|
||||
|
||||
_OUTPUT_MODEL_DIR = flags.DEFINE_string(
|
||||
'output_model_dir',
|
||||
None,
|
||||
'The output model dir.',
|
||||
required=True,
|
||||
)
|
||||
|
||||
# These hyperparameters defaults come from this tutorial by Hugging Face:
|
||||
# https://huggingface.co/docs/diffusers/training/text2image
|
||||
_LEARNING_RATE = flags.DEFINE_float(
|
||||
'learning_rate', 1e-5, 'The learning rate parameter for AdamW optimizer.'
|
||||
)
|
||||
|
||||
_BETA_1 = flags.DEFINE_float(
|
||||
'beta_1', 0.9, 'The beta_1 parameter for AdamW optimizer.'
|
||||
)
|
||||
|
||||
_BETA_2 = flags.DEFINE_float(
|
||||
'beta_2', 0.999, 'The beta_2 parameter for AdamW optimizer.'
|
||||
)
|
||||
|
||||
_WEIGHT_DECAY = flags.DEFINE_float(
|
||||
'weight_decay', 1e-2, 'The weight decay parameter for AdamW optimizer.'
|
||||
)
|
||||
|
||||
_EPSILON = flags.DEFINE_float(
|
||||
'epsilon', 1e-08, 'The epsilon parameter for AdamW optimizer.'
|
||||
)
|
||||
|
||||
RESOLUTION = int(os.environ.get('RESOLUTION', 512))
|
||||
|
||||
# The padding token and maximum prompt length are specific to the text encoder.
|
||||
# If you're using a different text encoder be sure to change them accordingly.
|
||||
PADDING_TOKEN = 49407
|
||||
MAX_PROMPT_LENGTH = 77
|
||||
|
||||
AUTO = tf.data.AUTOTUNE
|
||||
POS_IDS = tf.convert_to_tensor([list(range(MAX_PROMPT_LENGTH))], dtype=tf.int32)
|
||||
|
||||
|
||||
augmenter = keras.Sequential(
|
||||
layers=[
|
||||
keras_cv.layers.CenterCrop(RESOLUTION, RESOLUTION),
|
||||
keras_cv.layers.RandomFlip(),
|
||||
tf.keras.layers.Rescaling(scale=1.0 / 127.5, offset=-1),
|
||||
]
|
||||
)
|
||||
text_encoder = TextEncoder(MAX_PROMPT_LENGTH)
|
||||
|
||||
|
||||
def process_image(image_path, tokenized_text):
|
||||
image = tf.io.read_file(image_path)
|
||||
image = tf.io.decode_png(image, 3)
|
||||
image = tf.image.resize(image, (RESOLUTION, RESOLUTION))
|
||||
return image, tokenized_text
|
||||
|
||||
|
||||
def apply_augmentation(image_batch, token_batch):
|
||||
return augmenter(image_batch), token_batch
|
||||
|
||||
|
||||
def run_text_encoder(image_batch, token_batch):
|
||||
return (
|
||||
image_batch,
|
||||
token_batch,
|
||||
text_encoder([token_batch, POS_IDS], training=False),
|
||||
)
|
||||
|
||||
|
||||
def prepare_dict(image_batch, token_batch, encoded_text_batch):
|
||||
return {
|
||||
'images': image_batch,
|
||||
'tokens': token_batch,
|
||||
'encoded_text': encoded_text_batch,
|
||||
}
|
||||
|
||||
|
||||
def prepare_dataset(image_paths, tokenized_texts, batch_size=1):
|
||||
dataset = tf.data.Dataset.from_tensor_slices((image_paths, tokenized_texts))
|
||||
dataset = dataset.shuffle(batch_size * 10)
|
||||
dataset = dataset.map(process_image, num_parallel_calls=AUTO).batch(
|
||||
batch_size
|
||||
)
|
||||
dataset = dataset.map(apply_augmentation, num_parallel_calls=AUTO)
|
||||
dataset = dataset.map(run_text_encoder, num_parallel_calls=AUTO)
|
||||
dataset = dataset.map(prepare_dict, num_parallel_calls=AUTO)
|
||||
return dataset.prefetch(AUTO)
|
||||
|
||||
|
||||
def prepare_training_dataset(dataset_csv):
|
||||
"""Prepares training datasets."""
|
||||
if dataset_csv.startswith(constants.GCS_URI_PREFIX):
|
||||
if not os.path.exists(constants.LOCAL_DATA_DIR):
|
||||
os.makedirs(constants.LOCAL_DATA_DIR)
|
||||
logging.info(
|
||||
'Start to download data from %s to %s.',
|
||||
os.path.dirname(dataset_csv),
|
||||
constants.LOCAL_DATA_DIR,
|
||||
)
|
||||
fileutils.download_gcs_dir_to_local(
|
||||
os.path.dirname(dataset_csv), constants.LOCAL_DATA_DIR
|
||||
)
|
||||
data_frame = pd.read_csv(
|
||||
os.path.join(constants.LOCAL_DATA_DIR, os.path.basename(dataset_csv))
|
||||
)
|
||||
data_frame['image_path'] = data_frame['image_path'].apply(
|
||||
lambda x: os.path.join(constants.LOCAL_DATA_DIR, x)
|
||||
)
|
||||
else:
|
||||
# Keeps the following codes for experiments with
|
||||
# https://keras.io/examples/generative/finetune_stable_diffusion/.
|
||||
data_path = tf.keras.utils.get_file(origin=dataset_csv, untar=True)
|
||||
data_frame = pd.read_csv(os.path.join(data_path, 'data.csv'))
|
||||
data_frame['image_path'] = data_frame['image_path'].apply(
|
||||
lambda x: os.path.join(data_path, x)
|
||||
)
|
||||
data_frame.head()
|
||||
|
||||
# Load the tokenizer.
|
||||
tokenizer = SimpleTokenizer()
|
||||
|
||||
# Method to tokenize and pad the tokens.
|
||||
def process_text(caption):
|
||||
tokens = tokenizer.encode(caption)
|
||||
tokens = tokens + [PADDING_TOKEN] * (MAX_PROMPT_LENGTH - len(tokens))
|
||||
return np.array(tokens)
|
||||
|
||||
# Collate the tokenized captions into an array.
|
||||
tokenized_texts = np.empty((len(data_frame), MAX_PROMPT_LENGTH))
|
||||
|
||||
all_captions = list(data_frame['caption'].values)
|
||||
for i, caption in enumerate(all_captions):
|
||||
tokenized_texts[i] = process_text(caption)
|
||||
|
||||
# Prepare the dataset.
|
||||
training_dataset = prepare_dataset(
|
||||
np.array(data_frame['image_path']), tokenized_texts, batch_size=4
|
||||
)
|
||||
|
||||
return training_dataset
|
||||
|
||||
|
||||
class Trainer(tf.keras.Model):
|
||||
"""The trainer for Keras Stable Diffusion."""
|
||||
|
||||
# Reference:
|
||||
# https://github.com/huggingface/diffusers/blob/main/examples/text_to_image/train_text_to_image.py
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
diffusion_model,
|
||||
vae,
|
||||
noise_scheduler,
|
||||
use_mixed_precision=False,
|
||||
max_grad_norm=1.0,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.diffusion_model = diffusion_model
|
||||
self.vae = vae
|
||||
self.noise_scheduler = noise_scheduler
|
||||
self.max_grad_norm = max_grad_norm
|
||||
|
||||
self.use_mixed_precision = use_mixed_precision
|
||||
self.vae.trainable = False
|
||||
|
||||
def train_step(self, inputs):
|
||||
images = inputs['images']
|
||||
encoded_text = inputs['encoded_text']
|
||||
batch_size = tf.shape(images)[0]
|
||||
|
||||
with tf.GradientTape() as tape:
|
||||
# Project image into the latent space and sample from it.
|
||||
latents = self.sample_from_encoder_outputs(
|
||||
self.vae(images, training=False)
|
||||
)
|
||||
# Know more about the magic number here:
|
||||
# https://keras.io/examples/generative/fine_tune_via_textual_inversion/
|
||||
latents = latents * 0.18215
|
||||
|
||||
# Sample noise that we'll add to the latents.
|
||||
noise = tf.random.normal(tf.shape(latents))
|
||||
|
||||
# Sample a random timestep for each image.
|
||||
timesteps = tnp.random.randint(
|
||||
0, self.noise_scheduler.train_timesteps, (batch_size,)
|
||||
)
|
||||
|
||||
# Add noise to the latents according to the noise magnitude at each
|
||||
# timestep (this is the forward diffusion process).
|
||||
noisy_latents = self.noise_scheduler.add_noise(
|
||||
tf.cast(latents, noise.dtype), noise, timesteps
|
||||
)
|
||||
|
||||
# Get the target for loss depending on the prediction type
|
||||
# just the sampled noise for now.
|
||||
target = noise # noise_schedule.predict_epsilon == True
|
||||
|
||||
# Predict the noise residual and compute loss.
|
||||
# pylint: disable=unnecessary-lambda
|
||||
timestep_embedding = tf.map_fn(
|
||||
lambda t: self.get_timestep_embedding(t), timesteps, dtype=tf.float32
|
||||
)
|
||||
timestep_embedding = tf.squeeze(timestep_embedding, 1)
|
||||
model_pred = self.diffusion_model(
|
||||
[noisy_latents, timestep_embedding, encoded_text], training=True
|
||||
)
|
||||
loss = self.compiled_loss(target, model_pred)
|
||||
if self.use_mixed_precision:
|
||||
loss = self.optimizer.get_scaled_loss(loss)
|
||||
|
||||
# Update parameters of the diffusion model.
|
||||
trainable_vars = self.diffusion_model.trainable_variables
|
||||
gradients = tape.gradient(loss, trainable_vars)
|
||||
if self.use_mixed_precision:
|
||||
gradients = self.optimizer.get_unscaled_gradients(gradients)
|
||||
gradients = [tf.clip_by_norm(g, self.max_grad_norm) for g in gradients]
|
||||
self.optimizer.apply_gradients(zip(gradients, trainable_vars))
|
||||
|
||||
return {m.name: m.result() for m in self.metrics}
|
||||
|
||||
def get_timestep_embedding(self, timestep, dim=320, max_period=10000):
|
||||
half = dim // 2
|
||||
log_max_preiod = tf.math.log(tf.cast(max_period, tf.float32))
|
||||
# The docker builds could not support unary `-`.
|
||||
# pylint: disable=invalid-unary-operand-type
|
||||
freqs = tf.math.exp(
|
||||
-log_max_preiod * tf.range(0, half, dtype=tf.float32) / half
|
||||
)
|
||||
args = tf.convert_to_tensor([timestep], dtype=tf.float32) * freqs
|
||||
embedding = tf.concat([tf.math.cos(args), tf.math.sin(args)], 0)
|
||||
embedding = tf.reshape(embedding, [1, -1])
|
||||
return embedding
|
||||
|
||||
def sample_from_encoder_outputs(self, outputs):
|
||||
mean, logvar = tf.split(outputs, 2, axis=-1)
|
||||
logvar = tf.clip_by_value(logvar, -30.0, 20.0)
|
||||
std = tf.exp(0.5 * logvar)
|
||||
sample = tf.random.normal(tf.shape(mean), dtype=mean.dtype)
|
||||
return mean + std * sample
|
||||
|
||||
def save_weights(
|
||||
self, filepath, overwrite=True, save_format=None, options=None
|
||||
):
|
||||
# Overriding this method will allow us to use the `ModelCheckpoint`
|
||||
# callback directly with this trainer class. In this case, it will
|
||||
# only checkpoint the `diffusion_model` since that's what we're training
|
||||
# during fine-tuning.
|
||||
self.diffusion_model.save_weights(
|
||||
filepath=filepath,
|
||||
overwrite=overwrite,
|
||||
save_format=save_format,
|
||||
options=options,
|
||||
)
|
||||
|
||||
|
||||
def main(_) -> None:
|
||||
# _INPUT_CSV_PATH and _OUTPUT_MODEL_DIR should have the format as
|
||||
# gs://<bucket_name>/<object_name>.
|
||||
if _INPUT_CSV_PATH.value:
|
||||
if not _INPUT_CSV_PATH.value.startswith(constants.GCS_URI_PREFIX):
|
||||
raise ValueError('The input csv path should be a gcs path like gs://<>')
|
||||
if _OUTPUT_MODEL_DIR.value:
|
||||
if not _OUTPUT_MODEL_DIR.value.startswith(constants.GCS_URI_PREFIX):
|
||||
raise ValueError('The output model dir should be a gcs path like gs://<>')
|
||||
|
||||
if _USE_MP.value:
|
||||
keras.mixed_precision.set_global_policy('mixed_float16')
|
||||
|
||||
image_encoder = ImageEncoder(RESOLUTION, RESOLUTION)
|
||||
diffusion_ft_trainer = Trainer(
|
||||
diffusion_model=DiffusionModel(RESOLUTION, RESOLUTION, MAX_PROMPT_LENGTH),
|
||||
# Remove the top layer from the encoder, which cuts off the variance and
|
||||
# only returns the mean.
|
||||
vae=tf.keras.Model(
|
||||
image_encoder.input,
|
||||
image_encoder.layers[-2].output,
|
||||
),
|
||||
noise_scheduler=NoiseScheduler(),
|
||||
use_mixed_precision=_USE_MP.value,
|
||||
)
|
||||
|
||||
optimizer = tf.keras.optimizers.experimental.AdamW(
|
||||
learning_rate=_LEARNING_RATE.value,
|
||||
weight_decay=_WEIGHT_DECAY.value,
|
||||
beta_1=_BETA_1.value,
|
||||
beta_2=_BETA_2.value,
|
||||
epsilon=_EPSILON.value,
|
||||
)
|
||||
diffusion_ft_trainer.compile(optimizer=optimizer, loss='mse')
|
||||
|
||||
training_dataset = prepare_training_dataset(_INPUT_CSV_PATH.value)
|
||||
|
||||
# Note: gcsfuse does not work for Keras. We saves the trained models locally
|
||||
# first, and then copy to gcs storages.
|
||||
if not os.path.exists(constants.LOCAL_MODEL_DIR):
|
||||
os.makedirs(constants.LOCAL_MODEL_DIR)
|
||||
# The default saved model is in HDF5.
|
||||
ckpt_path = os.path.join(constants.LOCAL_MODEL_DIR, 'saved_model.h5')
|
||||
ckpt_callback = tf.keras.callbacks.ModelCheckpoint(
|
||||
ckpt_path,
|
||||
save_weights_only=True,
|
||||
monitor='loss',
|
||||
mode='min',
|
||||
)
|
||||
diffusion_ft_trainer.fit(
|
||||
training_dataset, epochs=_EPOCHS.value, callbacks=[ckpt_callback]
|
||||
)
|
||||
|
||||
# Copies the files in constants.LOCAL_MODEL_DIR to output_model_dir.
|
||||
fileutils.upload_local_dir_to_gcs(
|
||||
constants.LOCAL_MODEL_DIR, _OUTPUT_MODEL_DIR.value
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(main)
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# Dockerfile for serving dockers for transformers.
|
||||
#
|
||||
# To build:
|
||||
# docker build -f model_oss/transformers/dockerfile/serve.Dockerfile . -t ${YOUR_IMAGE_TAG}
|
||||
#
|
||||
# To push to gcr:
|
||||
# docker tag ${YOUR_IMAGE_TAG} gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
# docker push gcr.io/${YOUR_PROJECT}/${YOUR_IMAGE_TAG}
|
||||
# Switch to this base image for gpu serve.
|
||||
|
||||
FROM pytorch/torchserve:0.7.0-gpu
|
||||
|
||||
USER root
|
||||
|
||||
ENV infer_port=7080
|
||||
ENV mng_port=7081
|
||||
ENV model_name="transformers_serving"
|
||||
ENV PATH="/home/model-server/:${PATH}"
|
||||
|
||||
# Install libraries.
|
||||
ENV PIP_ROOT_USER_ACTION=ignore
|
||||
RUN python3 -m pip install --upgrade pip
|
||||
RUN pip install torch==1.13.1
|
||||
RUN pip install torchvision==0.14.1
|
||||
RUN pip install transformers==4.27.4
|
||||
RUN pip install datasets==2.9.0
|
||||
RUN pip install accelerate==0.17.0
|
||||
RUN pip install triton==2.0.0.dev20221120
|
||||
RUN pip install xformers==0.0.16
|
||||
RUN pip install google-cloud-storage==2.7.0
|
||||
RUN pip install absl-py==1.4.0
|
||||
|
||||
# Install libraries for document-question-answering.
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y --no-install-recommends tesseract-ocr
|
||||
RUN pip install tesseract==0.1.3
|
||||
RUN pip install pytesseract==0.3.10
|
||||
|
||||
# Install tools.
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
wget \
|
||||
vim
|
||||
|
||||
# Copy license.
|
||||
RUN wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/LICENSE
|
||||
|
||||
# Copy model artifacts.
|
||||
COPY model_oss/transformers/handler.py /home/model-server/handler.py
|
||||
COPY model_oss/util/ /home/model-server/util/
|
||||
ENV PYTHONPATH /home/model-server/
|
||||
|
||||
# Create torchserve configuration file.
|
||||
RUN echo \
|
||||
"default_response_timeout=1800\n" \
|
||||
"service_envelope=json\n" \
|
||||
"inference_address=http://0.0.0.0:${infer_port}\n" \
|
||||
"management_address=http://0.0.0.0:${mng_port}" >> /home/model-server/config.properties
|
||||
|
||||
# Expose ports.
|
||||
EXPOSE ${infer_port}
|
||||
EXPOSE ${mng_port}
|
||||
|
||||
# Archive model artifacts and dependencies.
|
||||
# Do not set --model-file and --serialized-file because model and checkpoint will be dynamically loaded in handler.py.
|
||||
RUN torch-model-archiver \
|
||||
--model-name=${model_name} \
|
||||
--version=1.0 \
|
||||
--handler=/home/model-server/handler.py \
|
||||
--runtime=python3 \
|
||||
--export-path=/home/model-server/model-store \
|
||||
--archive-format=default \
|
||||
--force
|
||||
|
||||
# Run Torchserve HTTP serve to respond to prediction requests.
|
||||
CMD ["torchserve", "--start", \
|
||||
"--ts-config", "/home/model-server/config.properties", \
|
||||
"--models", "${model_name}=${model_name}.mar", \
|
||||
"--model-store", "/home/model-server/model-store"]
|
||||
@@ -0,0 +1,233 @@
|
||||
"""Custom handler for huggingface/transformers models."""
|
||||
|
||||
# pylint: disable=g-multiple-import
|
||||
# pylint: disable=g-importing-member
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
from PIL import Image
|
||||
import torch
|
||||
from transformers import (
|
||||
AutoProcessor,
|
||||
AutoTokenizer,
|
||||
Blip2ForConditionalGeneration,
|
||||
Blip2Processor,
|
||||
BlipForConditionalGeneration,
|
||||
BlipForQuestionAnswering,
|
||||
BlipProcessor,
|
||||
CLIPModel,
|
||||
)
|
||||
from transformers import pipeline
|
||||
from ts.torch_handler.base_handler import BaseHandler
|
||||
|
||||
from util import constants
|
||||
from util import fileutils
|
||||
from util import image_format_converter
|
||||
|
||||
DEFAULT_MODEL_ID = "openai/clip-vit-base-patch32"
|
||||
SALESFORCE_BLIP = "Salesforce/blip"
|
||||
SALESFORCE_BLIP2 = "Salesforce/blip2"
|
||||
FLAN_T5 = "flan-t5"
|
||||
BART_LARGE_CNN = "facebook/bart-large-cnn"
|
||||
|
||||
ZERO_CLASSIFICATION = "zero-shot-image-classification"
|
||||
FEATURE_EMBEDDING = "feature-embedding"
|
||||
ZERO_DETECTION = "zero-shot-object-detection"
|
||||
IMAGE_CAPTIONING = "image-to-text"
|
||||
VQA = "visual-question-answering"
|
||||
DQA = "document-question-answering"
|
||||
SUMMARIZATION = "summarization"
|
||||
SUMMARIZATION_TEMPLATE = (
|
||||
"Summarize the following news article:\n{input}\nSummary:\n"
|
||||
)
|
||||
|
||||
|
||||
class TransformersHandler(BaseHandler):
|
||||
"""Custom handler for huggingface/transformers models."""
|
||||
|
||||
def initialize(self, context: Any):
|
||||
"""Custom initialize."""
|
||||
|
||||
properties = context.system_properties
|
||||
self.map_location = (
|
||||
"cuda"
|
||||
if torch.cuda.is_available() and properties.get("gpu_id") is not None
|
||||
else "cpu"
|
||||
)
|
||||
self.device = torch.device(
|
||||
self.map_location + ":" + str(properties.get("gpu_id"))
|
||||
if torch.cuda.is_available() and properties.get("gpu_id") is not None
|
||||
else self.map_location
|
||||
)
|
||||
|
||||
self.manifest = context.manifest
|
||||
# The model id is can be either:
|
||||
# 1) a huggingface model card id, like "Salesforce/blip", or
|
||||
# 2) a GCS path to the model files, like "gs://foo/bar".
|
||||
# If it's a model card id, the model will be loaded from huggingface.
|
||||
self.model_id = (
|
||||
DEFAULT_MODEL_ID
|
||||
if os.environ.get("MODEL_ID") is None
|
||||
else os.environ["MODEL_ID"]
|
||||
)
|
||||
# Else it will be downloaded from GCS to local first.
|
||||
# Since the transformers from_pretrained API can't read from GCS.
|
||||
if self.model_id.startswith(constants.GCS_URI_PREFIX):
|
||||
gcs_path = self.model_id[len(constants.GCS_URI_PREFIX) :]
|
||||
local_model_dir = os.path.join(constants.LOCAL_MODEL_DIR, gcs_path)
|
||||
logging.info("Download %s to %s", self.model_id, local_model_dir)
|
||||
fileutils.download_gcs_dir_to_local(self.model_id, local_model_dir)
|
||||
self.model_id = local_model_dir
|
||||
|
||||
self.task = (
|
||||
ZERO_CLASSIFICATION
|
||||
if os.environ.get("TASK") is None
|
||||
else os.environ["TASK"]
|
||||
)
|
||||
logging.info(
|
||||
"Handler initializing task:%s, model:%s", self.task, self.model_id
|
||||
)
|
||||
|
||||
if SALESFORCE_BLIP in self.model_id:
|
||||
# pipeline() hasn't been ready for Salesforce/blip models.
|
||||
self.salesforce_blip = True
|
||||
self._create_blip_model()
|
||||
else:
|
||||
self.salesforce_blip = False
|
||||
if self.task == FEATURE_EMBEDDING:
|
||||
self.model = CLIPModel.from_pretrained(self.model_id).to(
|
||||
self.map_location
|
||||
)
|
||||
self.tokenizer = AutoTokenizer.from_pretrained(self.model_id)
|
||||
self.processor = AutoProcessor.from_pretrained(self.model_id)
|
||||
elif self.task == SUMMARIZATION and FLAN_T5 in self.model_id:
|
||||
self.pipeline = pipeline(
|
||||
task=self.task,
|
||||
model=self.model_id,
|
||||
torch_dtype=torch.bfloat16,
|
||||
device_map="auto",
|
||||
)
|
||||
else:
|
||||
self.pipeline = pipeline(
|
||||
task=self.task, model=self.model_id, device=self.device
|
||||
)
|
||||
|
||||
self.initialized = True
|
||||
logging.info("Handler initialization done.")
|
||||
|
||||
def _create_blip_model(self):
|
||||
"""A helper for creating BLIP and BLIP2 models."""
|
||||
if SALESFORCE_BLIP2 in self.model_id:
|
||||
self.torch_type = torch.float16
|
||||
self.processor = Blip2Processor.from_pretrained(self.model_id)
|
||||
self.model = Blip2ForConditionalGeneration.from_pretrained(
|
||||
self.model_id, torch_dtype=self.torch_type
|
||||
).to(self.map_location)
|
||||
else:
|
||||
self.torch_type = torch.float32
|
||||
self.processor = BlipProcessor.from_pretrained(self.model_id)
|
||||
if self.task == IMAGE_CAPTIONING:
|
||||
self.model = BlipForConditionalGeneration.from_pretrained(
|
||||
self.model_id
|
||||
).to(self.map_location)
|
||||
elif self.task == VQA:
|
||||
self.model = BlipForQuestionAnswering.from_pretrained(self.model_id).to(
|
||||
self.map_location
|
||||
)
|
||||
|
||||
def _reformat_detection_result(self, data: List[Any]) -> List[Any]:
|
||||
"""Reformat zero-shot-object-detection output."""
|
||||
if not data:
|
||||
return [data]
|
||||
boxes = {}
|
||||
boxes["label"] = data[0]["label"]
|
||||
boxes["boxes"] = []
|
||||
for item in data:
|
||||
box = {}
|
||||
box["score"] = item["score"]
|
||||
box.update(item["box"])
|
||||
boxes["boxes"].append(box)
|
||||
outputs = [boxes]
|
||||
return outputs
|
||||
|
||||
def preprocess(
|
||||
self, data: Any
|
||||
) -> Tuple[Optional[List[str]], Optional[List[Image.Image]]]:
|
||||
"""Preprocess input data."""
|
||||
texts = None
|
||||
images = None
|
||||
if "text" in data[0]:
|
||||
texts = [item["text"] for item in data]
|
||||
if "image" in data[0]:
|
||||
images = [
|
||||
image_format_converter.base64_to_image(item["image"]) for item in data
|
||||
]
|
||||
return texts, images
|
||||
|
||||
def inference(self, data: Any, *args, **kwargs) -> List[Any]:
|
||||
"""Run the inference."""
|
||||
texts, images = data
|
||||
preds = None
|
||||
if self.task == ZERO_CLASSIFICATION:
|
||||
preds = self.pipeline(images=images, candidate_labels=texts)
|
||||
elif self.task == ZERO_DETECTION:
|
||||
# The object detection pipeline doesn't support batch prediction.
|
||||
preds = self.pipeline(image=images[0], candidate_labels=texts[0])
|
||||
elif self.task == IMAGE_CAPTIONING:
|
||||
if self.salesforce_blip:
|
||||
inputs = self.processor(images[0], return_tensors="pt").to(
|
||||
self.map_location, self.torch_type
|
||||
)
|
||||
preds = self.model.generate(**inputs)
|
||||
preds = [
|
||||
self.processor.decode(preds[0], skip_special_tokens=True).strip()
|
||||
]
|
||||
else:
|
||||
preds = self.pipeline(images=images)
|
||||
elif self.task == VQA:
|
||||
# The VQA pipelines doesn't support batch prediction.
|
||||
if self.salesforce_blip:
|
||||
inputs = self.processor(images[0], texts[0], return_tensors="pt").to(
|
||||
self.map_location, self.torch_type
|
||||
)
|
||||
preds = self.model.generate(**inputs)
|
||||
preds = [
|
||||
self.processor.decode(preds[0], skip_special_tokens=True).strip()
|
||||
]
|
||||
else:
|
||||
preds = self.pipeline(image=images[0], question=texts[0])
|
||||
elif self.task == DQA:
|
||||
# The DQA pipelines doesn't support batch prediction.
|
||||
preds = self.pipeline(image=images[0], question=texts[0])
|
||||
elif self.task == FEATURE_EMBEDDING:
|
||||
preds = {}
|
||||
if texts:
|
||||
inputs = self.tokenizer(
|
||||
text=texts, padding=True, return_tensors="pt"
|
||||
).to(self.map_location)
|
||||
text_features = self.model.get_text_features(**inputs)
|
||||
preds["text_features"] = text_features.detach().cpu().numpy().tolist()
|
||||
if images:
|
||||
inputs = self.processor(images=images, return_tensors="pt").to(
|
||||
self.map_location
|
||||
)
|
||||
image_features = self.model.get_image_features(**inputs)
|
||||
preds["image_features"] = image_features.detach().cpu().numpy().tolist()
|
||||
preds = [preds]
|
||||
elif self.task == SUMMARIZATION and FLAN_T5 in self.model_id:
|
||||
texts = [SUMMARIZATION_TEMPLATE.format(input=text) for text in texts]
|
||||
preds = self.pipeline(texts, max_length=130)
|
||||
elif self.task == SUMMARIZATION and self.model_id == BART_LARGE_CNN:
|
||||
preds = self.pipeline(
|
||||
texts[0], max_length=130, min_length=30, do_sample=False
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Invalid TASK: {self.task}")
|
||||
return preds
|
||||
|
||||
def postprocess(self, data: Any) -> List[Any]:
|
||||
if self.task == ZERO_DETECTION:
|
||||
data = self._reformat_detection_result(data)
|
||||
return data
|
||||
@@ -0,0 +1,64 @@
|
||||
"""Vertex vision model garden util constants."""
|
||||
|
||||
# Objectives.
|
||||
OBJECTIVE_IMAGE_CLASSIFICATION = 'icn'
|
||||
OBJECTIVE_IMAGE_OBJECT_DETECTION = 'iod'
|
||||
OBJECTIVE_IMAGE_SEGMENTATION = 'isg'
|
||||
|
||||
# Input file types.
|
||||
INPUT_FILE_TYPE_CSV = 'csv'
|
||||
INPUT_FILE_TYPE_JSONL = 'jsonl'
|
||||
INPUT_FILE_TYPE_COCO_JSON = 'coco_json'
|
||||
|
||||
# Output file types.
|
||||
OUTPUT_FILE_TYPE_TFRECORD = 'tfrecord'
|
||||
OUTPUT_FILE_TYPE_COCO_JSON = 'coco_json'
|
||||
|
||||
# Best evaluation metrics.
|
||||
IMAGE_CLASSIFICATION_SINGLE_LABEL_BEST_EVAL_METRIC = 'accuracy'
|
||||
IMAGE_CLASSIFICATION_MULTI_LABEL_BEST_EVAL_METRIC = 'meanPR-AUC'
|
||||
|
||||
IMAGE_OBJECT_DETECTION_BEST_EVAL_METRIC = 'AP50'
|
||||
IMAGE_SEGMENTATION_BEST_EVAL_METRIC = 'mean_iou'
|
||||
|
||||
VIDEO_CLASSIFICATION_BEST_EVAL_METRIC = 'accuracy'
|
||||
|
||||
# Best checkpoints.
|
||||
BEST_CKPT_DIRNAME = 'best_ckpt'
|
||||
BEST_CKPT_EVAL_FILENAME = 'info.json'
|
||||
BEST_CKPT_STEP_NAME = 'best_ckpt_global_step'
|
||||
BEST_CKPT_METRIC_COMP = 'higher'
|
||||
|
||||
# Reported hyperparameter tuning metric tag.
|
||||
HP_METRIC_TAG = 'model_performance'
|
||||
|
||||
# HPT trial prefix.
|
||||
TRIAL_PREFIX = 'trial_'
|
||||
|
||||
# ML uses from user input.
|
||||
ML_USE_TRAINING = 'training'
|
||||
ML_USE_VALIDATION = 'validation'
|
||||
ML_USE_TEST = 'test'
|
||||
|
||||
# COCO json keys
|
||||
COCO_JSON_ANNOTATIONS = 'annotations'
|
||||
COCO_JSON_ANNOTATION_IMAGE_ID = 'image_id'
|
||||
COCO_JSON_ANNOTATION_CATEGORY_ID = 'category_id'
|
||||
COCO_JSON_CATEGORIES = 'categories'
|
||||
COCO_JSON_CATEGORY_ID = 'id'
|
||||
COCO_JSON_CATEGORY_NAME = 'name'
|
||||
COCO_JSON_FILE_NAME = 'file_name'
|
||||
COCO_JSON_IMAGES = 'images'
|
||||
COCO_JSON_IMAGE_ID = 'id'
|
||||
COCO_JSON_IMAGE_WIDTH = 'width'
|
||||
COCO_JSON_IMAGE_HEIGHT = 'height'
|
||||
COCO_JSON_IMAGE_COCO_URL = 'coco_url'
|
||||
COCO_ANNOTATION_BBOX = 'bbox'
|
||||
|
||||
# GCS prefixes
|
||||
GCS_URI_PREFIX = 'gs://'
|
||||
GCSFUSE_URI_PREFIX = '/gcs/'
|
||||
|
||||
LOCAL_EVALUATION_RESULT_DIR = '/tmp/evaluation_result_dir'
|
||||
LOCAL_MODEL_DIR = '/tmp/model_dir'
|
||||
LOCAL_DATA_DIR = '/tmp/data'
|
||||
@@ -0,0 +1,79 @@
|
||||
"""Fileutil lib to copy files between gcs and local."""
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
from absl import logging
|
||||
from google.cloud import storage
|
||||
|
||||
from util import constants
|
||||
|
||||
|
||||
def download_gcs_file_to_local(gcs_uri: str, local_path: str):
|
||||
"""Download a gcs file to a local path.
|
||||
|
||||
Args:
|
||||
gcs_uri: A string of file path on GCS.
|
||||
local_path: A string of local file path.
|
||||
"""
|
||||
if not gcs_uri.startswith(constants.GCS_URI_PREFIX):
|
||||
raise ValueError(
|
||||
f'{gcs_uri} is not a GCS path starting with {constants.GCS_URI_PREFIX}.'
|
||||
)
|
||||
client = storage.Client()
|
||||
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
||||
with open(local_path, 'wb') as f:
|
||||
client.download_blob_to_file(gcs_uri, f)
|
||||
|
||||
|
||||
def download_gcs_dir_to_local(gcs_dir: str, local_dir: str):
|
||||
"""Downloads files in a GCS directory to a local directory.
|
||||
|
||||
For example:
|
||||
download_gcs_dir_to_local(gs://bucket/foo, /tmp/bar)
|
||||
gs://bucket/foo/a -> /tmp/bar/a
|
||||
gs://bucket/foo/b/c -> /tmp/bar/b/c
|
||||
|
||||
Arguments:
|
||||
gcs_dir: A string of directory path on GCS.
|
||||
local_dir: A string of local directory path.
|
||||
"""
|
||||
bucket_name = gcs_dir.split('/')[2]
|
||||
prefix = gcs_dir[len(constants.GCS_URI_PREFIX + bucket_name) :].strip('/')
|
||||
client = storage.Client()
|
||||
blobs = client.list_blobs(bucket_name, prefix=prefix)
|
||||
for blob in blobs:
|
||||
if blob.name[-1] == '/':
|
||||
continue
|
||||
file_path = blob.name[len(prefix) :].strip('/')
|
||||
local_file_path = os.path.join(local_dir, file_path)
|
||||
os.makedirs(os.path.dirname(local_file_path), exist_ok=True)
|
||||
logging.info('Downloading %s to %s', file_path, local_file_path)
|
||||
blob.download_to_filename(local_file_path)
|
||||
|
||||
|
||||
def upload_local_dir_to_gcs(local_dir: str, gcs_dir: str):
|
||||
"""Uploads local dir to gcs.
|
||||
|
||||
For example:
|
||||
upload_local_dir_to_gcs(/tmp/bar, gs://bucket/foo)
|
||||
gs://bucket/foo/a -> /tmp/bar/a
|
||||
gs://bucket/foo/b/c -> /tmp/bar/b/c
|
||||
|
||||
Arguments:
|
||||
local_dir: A string of local directory path.
|
||||
gcs_dir: A string of directory path on GCS.
|
||||
"""
|
||||
bucket_name = gcs_dir.split('/')[2]
|
||||
blob_dir = '/'.join(gcs_dir.split('/')[3:])
|
||||
client = storage.Client()
|
||||
bucket = client.bucket(bucket_name)
|
||||
for local_file in glob.glob(local_dir + '/**'):
|
||||
if os.path.isfile(local_file):
|
||||
logging.info(
|
||||
'Uploading %s to %s',
|
||||
local_file,
|
||||
os.path.join(constants.GCS_URI_PREFIX, bucket_name, blob_dir),
|
||||
)
|
||||
blob = bucket.blob(os.path.join(blob_dir, os.path.basename(local_file)))
|
||||
blob.upload_from_filename(local_file)
|
||||
@@ -0,0 +1,21 @@
|
||||
"""Utility functions for Vertex Hyperparameter Tuning Jobs."""
|
||||
|
||||
import os
|
||||
|
||||
from absl import logging
|
||||
|
||||
_ENVIRONMENT_VARIABLE_FOR_TRIAL_ID = 'CLOUD_ML_TRIAL_ID'
|
||||
|
||||
|
||||
def get_trial_id_from_environment() -> str:
|
||||
"""Gets the trial id from environment variable.
|
||||
|
||||
Returns:
|
||||
The trial id from environement or '0' if not found.
|
||||
"""
|
||||
if _ENVIRONMENT_VARIABLE_FOR_TRIAL_ID not in os.environ:
|
||||
logging.warning(
|
||||
'Environment variable %s not found, return 0 as default trial id.',
|
||||
_ENVIRONMENT_VARIABLE_FOR_TRIAL_ID,
|
||||
)
|
||||
return os.environ.get(_ENVIRONMENT_VARIABLE_FOR_TRIAL_ID, '0')
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Image format converter util lib."""
|
||||
|
||||
import base64
|
||||
import io
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
def image_to_base64(image: Image.Image) -> str:
|
||||
"""Convert a PIL image to a base64 string."""
|
||||
buffer = io.BytesIO()
|
||||
image.save(buffer, format="JPEG")
|
||||
image_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
|
||||
return image_str
|
||||
|
||||
|
||||
def base64_to_image(image_str: str) -> Image.Image:
|
||||
"""Convert a base64 string to a PIL image."""
|
||||
image = Image.open(io.BytesIO(base64.b64decode(image_str)))
|
||||
return image
|
||||
@@ -30,10 +30,54 @@
|
||||
/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/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/ml_ops/stage3/get_started_with_dataflow_flex_template_component.ipynb @wintwoo
|
||||
/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
|
||||
/notebooks/community/feature_store/get_started_vertex_feature_store.ipynb @junkourata
|
||||
/notebooks/community/model_garden/model_garden_huggingface_local_inference.ipynb @dstnluong-google
|
||||
/notebooks/community/model_garden/model_garden_mediapipe_image_classification.ipynb @schmidt-sebastian
|
||||
/notebooks/community/model_garden/model_garden_mediapipe_gesture_recognition.ipynb @schmidt-sebastian
|
||||
/notebooks/community/model_garden/model_garden_mediapipe_object_detection.ipynb @schmidt-sebastian
|
||||
/notebooks/community/model_garden/model_garden_mediapipe_text_classification.ipynb @schmidt-sebastian
|
||||
/notebooks/community/model_garden/model_garden_proprietary_image_classification.ipynb @weigary
|
||||
/notebooks/community/model_garden/model_garden_proprietary_image_object_detection.ipynb @weigary
|
||||
/notebooks/community/model_garden/model_garden_tfvision_image_classification.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_tfvision_image_object_detection.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_tfvision_image_segmentation.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_2_1.ipynb @bingatgoogle
|
||||
/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_instructpix2pix.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_controlnet.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_blip_image_captioning.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_blip_vqa.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_vilt_vqa.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_vit_gpt2_image_captioning.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_clip.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_owlvit.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_layoutml_document_qa.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_blip2.ipynb @xiangxu-google
|
||||
/notebooks/community/model_garden/model_garden_pytorch_detectron2.ipynb @lavraicse
|
||||
/notebooks/community/model_garden/model_garden_pytorch_dolly_v2.ipynb @lavraicse
|
||||
/notebooks/community/model_garden/model_garden_pytorch_bart_large_cnn.ipynb @lavraicse
|
||||
/notebooks/community/model_garden/model_garden_jax_vision_transformer.ipynb @lavraicse
|
||||
/notebooks/community/model_garden/model_garden_pytorch_text_to_video_zero_shot.ipynb @bingatgoogle
|
||||
/notebooks/community/model_garden/model_garden_pytorch_text_to_video.ipynb @KCFindstr
|
||||
/notebooks/community/generative_ai/text_embedding_api_semantic_search_with_scann.ipynb @henrytansetiawan
|
||||
/notebooks/community/bigquery_ml_inference/bq_ml_with_vision_translation_nlp.ipynb @deaconsmith
|
||||
/notebooks/community/model_garden/model_garden_keras_stable_diffusion.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_pytorch_sam.ipynb @huguensjean
|
||||
/notebooks/community/model_garden/model_garden_pytorch_pic2word.ipynb @jismailyan
|
||||
/notebooks/community/model_garden/model_garden_pytorch_peft.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_peft.ipynb @genquan9
|
||||
/notebooks/community/model_garden/model_garden_movinet_clip_classification.ipynb @KCFindstr
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
[Unstructured data analytics with BigQuery ML and Vertex AI pre-trained models](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/bigquery_ml/bq_ml_with_vision_translation_nlp.ipynb)
|
||||
|
||||
```
|
||||
Learn how to analyze unstructured data within BigQuery using BigQuery's inference engine. You will use BigQuery ML to connect to three pretrained Vertex AI APIs - Vision API, Translation API and Natural Language Processing API.
|
||||
|
||||
The steps performed include:
|
||||
|
||||
- Define pre-trained models for Vision AI, Translation AI and NLP AI in BigQuery ML
|
||||
- Call the Vision API (`ML.ANNOTATE_IMAGE`) to detect text in images stored in Cloud Storage
|
||||
You will need to create an object table in BigQuery to do this
|
||||
- Call the Translation API (`ML.TRANSLATE`) to detect the language of text, and translate non-English movie titles to English
|
||||
- Call the Natural Language API (`ML.UNDERSTAND_TEXT`) to run sentiment analysis over movie reviews stored in BigQuery
|
||||
|
||||
```
|
||||
|
||||
Check out the [blog for this notebook](https://cloud.google.com/blog/products/data-analytics/how-simplify-unstructured-data-analytics-using-bigquery-ml-and-vertex-ai).
|
||||
Learn more about [BigQuery ML inference engine](https://cloud.google.com/bigquery/docs/reference/standard-sql/inference-overview).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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
+373
@@ -0,0 +1,373 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "KSP1duKDeaDR",
|
||||
"metadata": {
|
||||
"id": "KSP1duKDeaDR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "67b2c5cc-8fc6-4082-9052-69fa0377d770",
|
||||
"metadata": {
|
||||
"id": "67b2c5cc-8fc6-4082-9052-69fa0377d770"
|
||||
},
|
||||
"source": [
|
||||
"# Semantic Search using Embeddings\n",
|
||||
"\n",
|
||||
"Semantic search is a type of search that uses the meaning of words and phrases to find relevant results.\n",
|
||||
"\n",
|
||||
"In this tutorial, we will demonstrate how to do semantic search with embeddings generated from the news text and using [Google ScaNN: Efficient Vector Similarity Search](https://ai.googleblog.com/2020/07/announcing-scann-efficient-vector.html) to retrieve the most relevant news semantically.\n",
|
||||
"\n",
|
||||
"## Pre-requisites:\n",
|
||||
"- Vertex LLM SDK\n",
|
||||
"- ScaNN [github](https://github.com/google-research/google-research/tree/master/scann)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "FyyMdUeAJIVv",
|
||||
"metadata": {
|
||||
"id": "FyyMdUeAJIVv"
|
||||
},
|
||||
"source": [
|
||||
"## Install Vertex LLM SDK\n",
|
||||
"\n",
|
||||
"DISCLAIMER: Text Embedding API is now in Experimental Preview. This release focuses on validating model prototypes and these models are not guaranteed to be released. Use of Text Embedding API is governed by the Google Cloud Terms of Service, the Pre-GA Offerings Terms of the GCP Service Specific Terms. The Acceptance Use Policy, and the Generative AI Prohibited Use Policy. Vertex Text Embedding API’s features may be unstable, change in backward-incompatible ways, and are not guaranteed to be released. There are no SLAs provided and no technical support obligations. GCP’s Cloud Data Processing Addendum does not apply to Pre-GA Offerings and customers should not use Text Embedding API to process personal data or other data subject to legal or regulatory compliance requirements. See description of launch stage for details.\n",
|
||||
"\n",
|
||||
"The information in this documentation is provided to the customer on an “as is” and “with all faults” basis without any warranty of any kind, either express or implied. Google does not warrant or guarantee the correctness, accuracy or reliability of the information in here. In no event will Google or its affiliates or licensors be liable for any damage or harm to customers from customer’s use of these materials."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "h6KaE3XRJdxc",
|
||||
"metadata": {
|
||||
"id": "h6KaE3XRJdxc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
"google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "snBUuUamoJPz",
|
||||
"metadata": {
|
||||
"id": "snBUuUamoJPz"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip3 install google-cloud-aiplatform>=1.25 \"shapely<2.0.0\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "zgVQcE0ewO8W",
|
||||
"metadata": {
|
||||
"id": "zgVQcE0ewO8W"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"cloud-nl-llm-embedding\" # @param {type:\"string\"}\n",
|
||||
"LOCATION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"import vertexai\n",
|
||||
"\n",
|
||||
"vertexai.init(project=PROJECT_ID, location=LOCATION)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4xFzXmPbY7FC",
|
||||
"metadata": {
|
||||
"id": "4xFzXmPbY7FC"
|
||||
},
|
||||
"source": [
|
||||
"**Attention**: you would need to restart runtime so that the right package is installed."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "xnfG88OPZI18",
|
||||
"metadata": {
|
||||
"id": "xnfG88OPZI18"
|
||||
},
|
||||
"source": [
|
||||
"## Import TextEmbeddingModel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1e6e1b98-a632-44a2-afb8-fc212018ef4f",
|
||||
"metadata": {
|
||||
"id": "1e6e1b98-a632-44a2-afb8-fc212018ef4f"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from vertexai.preview.language_models import TextEmbeddingModel\n",
|
||||
"\n",
|
||||
"model = TextEmbeddingModel.from_pretrained(\"textembedding-gecko@001\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1suA-1HuaGj6",
|
||||
"metadata": {
|
||||
"id": "1suA-1HuaGj6"
|
||||
},
|
||||
"source": [
|
||||
"## Install ScaNN Package"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "770255d3-54dd-48c8-bbdd-fbc0be41f085",
|
||||
"metadata": {
|
||||
"id": "770255d3-54dd-48c8-bbdd-fbc0be41f085"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install scann"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "dae340cb-0583-4e7e-a562-6817ee4d7f6d",
|
||||
"metadata": {
|
||||
"id": "dae340cb-0583-4e7e-a562-6817ee4d7f6d"
|
||||
},
|
||||
"source": [
|
||||
"## Imports packages"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "412d00f1-08db-4880-8ced-52a9583757b8",
|
||||
"metadata": {
|
||||
"id": "412d00f1-08db-4880-8ced-52a9583757b8"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import time\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import scann"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f50f22f3-ec85-463e-b6fe-5c8e6b80b07b",
|
||||
"metadata": {
|
||||
"id": "f50f22f3-ec85-463e-b6fe-5c8e6b80b07b"
|
||||
},
|
||||
"source": [
|
||||
"## Create Embedding Dataset.\n",
|
||||
"\n",
|
||||
"The dataset is solely to demonstrate the use of the Text Embedding API with a vector database. It is not intended to be used for any other purpose, such as evaluating models. The dataset is small and does not represent a comprehensive sample of all possible text."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "2OUg-Qf8iFta",
|
||||
"metadata": {
|
||||
"id": "2OUg-Qf8iFta"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!gsutil cp gs://cloud-samples-data/vertex-ai/dataset-management/datasets/bert_finetuning/wide_and_deep_trainer_container_tests_input.jsonl ."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "BNPapKXviHlE",
|
||||
"metadata": {
|
||||
"id": "BNPapKXviHlE"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"records = []\n",
|
||||
"with open(\"wide_and_deep_trainer_container_tests_input.jsonl\") as f:\n",
|
||||
" for line in f:\n",
|
||||
" record = json.loads(line)\n",
|
||||
" records.append(record)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "Z1Y9Bx2miJba",
|
||||
"metadata": {
|
||||
"id": "Z1Y9Bx2miJba"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Peek at the data.\n",
|
||||
"df = pd.DataFrame(records)\n",
|
||||
"df.head(50)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1v7zUwoWiPl-",
|
||||
"metadata": {
|
||||
"id": "1v7zUwoWiPl-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_embedding(text):\n",
|
||||
" get_embedding.counter += 1\n",
|
||||
" try:\n",
|
||||
" if get_embedding.counter % 100 == 0:\n",
|
||||
" time.sleep(3)\n",
|
||||
" return model.get_embeddings([text])[0].values\n",
|
||||
" except:\n",
|
||||
" return []\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"get_embedding.counter = 0\n",
|
||||
"\n",
|
||||
"# This may take several minutes to complete.\n",
|
||||
"df[\"embedding\"] = df[\"textContent\"].apply(lambda x: get_embedding(x))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ba4f49b6-65e1-49ea-988b-c4e195219deb",
|
||||
"metadata": {
|
||||
"id": "ba4f49b6-65e1-49ea-988b-c4e195219deb"
|
||||
},
|
||||
"source": [
|
||||
"## Create an Index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "245bc8cd-038b-484a-acb2-3a705d4cc4cf",
|
||||
"metadata": {
|
||||
"id": "245bc8cd-038b-484a-acb2-3a705d4cc4cf"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"record_count = len(records)\n",
|
||||
"dataset = np.empty((record_count, 768))\n",
|
||||
"for i in range(record_count):\n",
|
||||
" dataset[i] = df.embedding[i]\n",
|
||||
"\n",
|
||||
"normalized_dataset = dataset / np.linalg.norm(dataset, axis=1)[:, np.newaxis]\n",
|
||||
"# configure ScaNN as a tree - asymmetric hash hybrid with reordering\n",
|
||||
"# anisotropic quantization as described in the paper; see README\n",
|
||||
"\n",
|
||||
"# use scann.scann_ops.build() to instead create a TensorFlow-compatible searcher\n",
|
||||
"searcher = (\n",
|
||||
" scann.scann_ops_pybind.builder(normalized_dataset, 10, \"dot_product\")\n",
|
||||
" .tree(\n",
|
||||
" num_leaves=record_count,\n",
|
||||
" num_leaves_to_search=record_count,\n",
|
||||
" training_sample_size=record_count,\n",
|
||||
" )\n",
|
||||
" .score_ah(2, anisotropic_quantization_threshold=0.2)\n",
|
||||
" .reorder(100)\n",
|
||||
" .build()\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9f1689eb-c27c-4566-afc7-7fbc55552aad",
|
||||
"metadata": {
|
||||
"id": "9f1689eb-c27c-4566-afc7-7fbc55552aad"
|
||||
},
|
||||
"source": [
|
||||
"## Queries the Index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "494079df-d8f6-4a6a-b26b-0b0477791adc",
|
||||
"metadata": {
|
||||
"id": "494079df-d8f6-4a6a-b26b-0b0477791adc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def search(query):\n",
|
||||
" start = time.time()\n",
|
||||
" query = model.get_embeddings([query])[0].values\n",
|
||||
" neighbors, distances = searcher.search(query, final_num_neighbors=3)\n",
|
||||
" end = time.time()\n",
|
||||
"\n",
|
||||
" for id, dist in zip(neighbors, distances):\n",
|
||||
" print(f\"[docid:{id}] [{dist}] -- {df.textContent[int(id)][:125]}...\")\n",
|
||||
" print(\"Latency (ms):\", 1000 * (end - start))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "50497681-6112-4147-a13e-afedb72b54f5",
|
||||
"metadata": {
|
||||
"id": "50497681-6112-4147-a13e-afedb72b54f5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search(\"tell me about shark or animal\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c814f835-3e2a-4366-a334-ccc636f00b83",
|
||||
"metadata": {
|
||||
"id": "c814f835-3e2a-4366-a334-ccc636f00b83"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"search(\"tell me about an important moment or event in your life\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "text_embedding_api_semantic_search_with_scann.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -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": {
|
||||
|
||||
+24
@@ -560,6 +560,30 @@
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"source": [
|
||||
"#### Set service account access for Vertex AI Pipelines\n",
|
||||
"\n",
|
||||
"Run the following commands to grant your service account access to read and write pipeline artifacts in the bucket that you created in the previous step -- you only need to run these once per service account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectCreator $BUCKET_URI\n",
|
||||
"\n",
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectViewer $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
|
||||
+24
@@ -557,6 +557,30 @@
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"source": [
|
||||
"#### Set service account access for Vertex AI Pipelines\n",
|
||||
"\n",
|
||||
"Run the following commands to grant your service account access to read and write pipeline artifacts in the bucket that you created in the previous step -- you only need to run these once per service account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectCreator $BUCKET_URI\n",
|
||||
"\n",
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectViewer $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
|
||||
+25
-1
@@ -196,7 +196,7 @@
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install {USER_FLAG} --upgrade --quiet google-cloud-aiplatform \\\n",
|
||||
" google-cloud-pipeline-components \\\n",
|
||||
" google-cloud-pipeline-components==1.0.25 \\\n",
|
||||
" kfp "
|
||||
]
|
||||
},
|
||||
@@ -557,6 +557,30 @@
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"source": [
|
||||
"#### Set service account access for Vertex AI Pipelines\n",
|
||||
"\n",
|
||||
"Run the following commands to grant your service account access to read and write pipeline artifacts in the bucket that you created in the previous step -- you only need to run these once per service account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectCreator $BUCKET_URI\n",
|
||||
"\n",
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectViewer $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
|
||||
+832
@@ -0,0 +1,832 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "copyright"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "title:generic,gcp"
|
||||
},
|
||||
"source": [
|
||||
"# E2E ML on GCP: MLOps stage 3 : formalization: get started with the Dataflow Flex Template component\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage3/get_started_with_dataflow_pipeline_components.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://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage3/get_started_with_dataflow_pipeline_components.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://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/stage3/get_started_with_dataflow_pipeline_components.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",
|
||||
" \n",
|
||||
"</table>\n",
|
||||
"<br/><br/><br/>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "overview:mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This tutorial demonstrates how to use Vertex AI for E2E MLOps on Google Cloud in production. This tutorial covers stage 3 : formalization: get started with the Dataflow Flex Template component."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "objective:mlops,stage3,get_started_dataflow_pipeline_components"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn how to use prebuilt `Google Cloud Pipeline Components` to execute `Dataflow` [Flex Template](https://cloud.google.com/dataflow/docs/guides/templates/using-flex-templates) jobs.\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `Vertex AI Pipelines`\n",
|
||||
"- `Google Cloud Pipeline Components`\n",
|
||||
"- `Dataflow`\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Defining a pipeline step to execute a Dataflow Flex Template job within a Vertex AI pipeline.\n",
|
||||
"- Execute a Vertex AI pipeline."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dataset:gsod,lrg"
|
||||
},
|
||||
"source": [
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset used for this tutorial is the sample data from the [Apache Beam Mobile Gaming Pipeline example](https://beam.apache.org/get-started/mobile-gaming-example/)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c997d8d92ce"
|
||||
},
|
||||
"source": [
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"* Dataflow\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and [Dataflow pricing](https://cloud.google.com/dataflow/pricing)\n",
|
||||
"and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Installations\n",
|
||||
"\n",
|
||||
"Install the required packages for executing the notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install --pre --upgrade google-cloud-pipeline-components $USER_FLAG -q"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"source": [
|
||||
"### Restart the kernel\n",
|
||||
"\n",
|
||||
"Once you've installed the additional packages, you need to restart the notebook kernel so it can find the packages."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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 API and Dataflow API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,dataflow.googleapis.com).\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`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_project_id"
|
||||
},
|
||||
"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": "250cb8c648d5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### Region\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",
|
||||
"\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": "927085b84a07"
|
||||
},
|
||||
"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",
|
||||
"**Click Create service account**.\n",
|
||||
"\n",
|
||||
"In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"\n",
|
||||
"In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex\" into the filter box, and select **Vertex Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"\n",
|
||||
"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": "89788a802687"
|
||||
},
|
||||
"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": "40ed98f5cc48"
|
||||
},
|
||||
"source": [
|
||||
"#### If you are using Colab Notebooks, set the project using gcloud config."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fde1a355f1e9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" ! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bucket:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"When you initialize the Vertex SDK for Python, you specify a Cloud Storage staging bucket. The staging bucket is where all the data associated with your dataset and model resources are retained across sessions.\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_URI = \"gs://[your-bucket-name]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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_URI = \"gs://\" + PROJECT_ID + \"aip-\" + TIMESTAMP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account"
|
||||
},
|
||||
"source": [
|
||||
"#### Service Account\n",
|
||||
"\n",
|
||||
"**If you don't know your service account**, try to get your service account using `gcloud` command by executing the second cell below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"SERVICE_ACCOUNT = \"[your-service-account]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_service_account"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if (\n",
|
||||
" SERVICE_ACCOUNT == \"\"\n",
|
||||
" or SERVICE_ACCOUNT is None\n",
|
||||
" or SERVICE_ACCOUNT == \"[your-service-account]\"\n",
|
||||
"):\n",
|
||||
" # Get your service account from gcloud\n",
|
||||
" if not IS_COLAB:\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].replace(\"*\", \"\").strip()\n",
|
||||
"\n",
|
||||
" if IS_COLAB:\n",
|
||||
" shell_output = ! gcloud projects describe $PROJECT_ID\n",
|
||||
" project_number = shell_output[-1].split(\":\")[1].strip().replace(\"'\", \"\")\n",
|
||||
" SERVICE_ACCOUNT = f\"{project_number}-compute@developer.gserviceaccount.com\"\n",
|
||||
"\n",
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"source": [
|
||||
"#### Set service account access for Vertex AI Pipelines\n",
|
||||
"\n",
|
||||
"Run the following commands to grant your service account access to read and write pipeline artifacts in the bucket that you created in the previous step -- you only need to run these once per service account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectCreator $BUCKET_URI\n",
|
||||
"\n",
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectViewer $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "setup_vars"
|
||||
},
|
||||
"source": [
|
||||
"### Set up variables\n",
|
||||
"\n",
|
||||
"Next, set up some variables used throughout the tutorial.\n",
|
||||
"### Import libraries and define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "import_aip:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import google.cloud.aiplatform as aip\n",
|
||||
"from google_cloud_pipeline_components.experimental.dataflow import \\\n",
|
||||
" DataflowFlexTemplateJobOp\n",
|
||||
"from google_cloud_pipeline_components.v1.wait_gcp_resources import \\\n",
|
||||
" WaitGcpResourcesOp\n",
|
||||
"from kfp import dsl\n",
|
||||
"from kfp.v2 import compiler"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"aip.init(project=PROJECT_ID, staging_bucket=BUCKET_URI)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "writefile:wc.py"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare a Flex Template job to convert a CSV file to Parquet\n",
|
||||
"\n",
|
||||
"In this tutorial, you use the Google-provided [File Format Conversion template](https://cloud.google.com/dataflow/docs/guides/templates/provided/file-format-conversion) to convert a CSV file to Parquet format."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "writefile:requirements,wc"
|
||||
},
|
||||
"source": [
|
||||
"#### Create the Avro schema file\n",
|
||||
"\n",
|
||||
"First, create an Avro schema file that describes the example data."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "writefile:requirements,wc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"%%writefile gaming_schema.avsc\n",
|
||||
"\n",
|
||||
"{\n",
|
||||
" \"type\" : \"record\",\n",
|
||||
" \"name\" : \"user_score\",\n",
|
||||
" \"fields\" : [\n",
|
||||
" { \"name\" : \"user\" , \"type\" : \"string\" },\n",
|
||||
" { \"name\" : \"team\" , \"type\" : \"string\" },\n",
|
||||
" { \"name\" : \"score\" , \"type\" : \"int\" },\n",
|
||||
" { \"name\" : \"ts_epoch\" , \"type\" : \"long\" },\n",
|
||||
" { \"name\" : \"ts_str\" , \"type\" : \"string\" }\n",
|
||||
" ]\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "copy_to_gcs:wc"
|
||||
},
|
||||
"source": [
|
||||
"#### Copy the Avro schema file to Cloud Storage\n",
|
||||
"\n",
|
||||
"Next, you copy the Avro schema file to your Cloud Storage bucket.\n",
|
||||
"\n",
|
||||
"Additional, you set the following:\n",
|
||||
"\n",
|
||||
"- The Cloud Storage location of the Flex Template definition file. In this tutorial, you use the location of the File Conversion template.\n",
|
||||
"- A `Dict` containing the user parameters required by the Flex Template. These parameters include:\n",
|
||||
"\n",
|
||||
" - `inputFileFormat`: The file format of the input files.\n",
|
||||
" - `outputFileFormat`: The file format of the output files.\n",
|
||||
" - `inputFileSpec`: The input filepattern to read from.\n",
|
||||
" - `outputBucket`: The Cloud Storage path to write the output files.\n",
|
||||
" - `schema`: The Cloud Storage path top the avro schema file used for the conversion.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "copy_to_gcs:wc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"GCS_AVRO_SCHEMA = BUCKET_URI + \"/gaming_schema.avsc\"\n",
|
||||
"! gsutil cp gaming_schema.avsc $GCS_AVRO_SCHEMA\n",
|
||||
"\n",
|
||||
"GCS_FLEX_TEMPLATE_PATH = \"gs://dataflow-templates/latest/flex/File_Format_Conversion\"\n",
|
||||
"GCS_CONVERT_IN = \"gs://dataflow-samples/game/5000_gaming_data.csv\"\n",
|
||||
"GCS_CONVERT_OUT = BUCKET_URI + \"/parquet_out/\"\n",
|
||||
"\n",
|
||||
"TEMPLATE_PARAMETERS = {\n",
|
||||
" \"inputFileFormat\": \"csv\",\n",
|
||||
" \"outputFileFormat\": \"parquet\",\n",
|
||||
" \"inputFileSpec\": GCS_CONVERT_IN,\n",
|
||||
" \"outputBucket\": GCS_CONVERT_OUT,\n",
|
||||
" \"schema\": GCS_AVRO_SCHEMA,\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "create_dataflow_pipeline:wc"
|
||||
},
|
||||
"source": [
|
||||
"### Create and execute the pipeline job\n",
|
||||
"\n",
|
||||
"In this example, the `DataflowFlexTemplateJobOp` component takes the following parameters:\n",
|
||||
"\n",
|
||||
"- `project_id`: The project ID.\n",
|
||||
"- `location`: The region.\n",
|
||||
"- `container_spec_gcs_path`: The Cloud Storage path to a file that contains the Flex Template definition. This file contains a json serialized `ContainerSpec` as content.\n",
|
||||
"- `temp_location`: The Cloud Storage path to use for temporary files.\n",
|
||||
"- `parameters`: The parameters for the Flex Template.\n",
|
||||
"\n",
|
||||
"Learn more about [Google Cloud Pipeline Components for Dataflow](https://google-cloud-pipeline-components.readthedocs.io/en/google-cloud-pipeline-components-2.0.0b2/google_cloud_pipeline_components.experimental.dataflow.html)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "create_dataflow_pipeline:wc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PIPELINE_ROOT = \"{}/pipeline_root/dataflow_file_conversion\".format(BUCKET_URI)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"@dsl.pipeline(\n",
|
||||
" name=\"dataflow-file-conversion\", description=\"Dataflow file format conversion\"\n",
|
||||
")\n",
|
||||
"def pipeline(\n",
|
||||
" project_id: str = PROJECT_ID,\n",
|
||||
" location: str = REGION,\n",
|
||||
" container_spec_gcs_path: str = GCS_FLEX_TEMPLATE_PATH,\n",
|
||||
" temp_location: str = PIPELINE_ROOT,\n",
|
||||
" parameters: dict = TEMPLATE_PARAMETERS,\n",
|
||||
"):\n",
|
||||
" flex_template_op = DataflowFlexTemplateJobOp(\n",
|
||||
" project=project_id,\n",
|
||||
" location=location,\n",
|
||||
" container_spec_gcs_path=container_spec_gcs_path,\n",
|
||||
" temp_location=temp_location,\n",
|
||||
" parameters=parameters,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" _ = WaitGcpResourcesOp(gcp_resources=flex_template_op.outputs[\"gcp_resources\"])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"compiler.Compiler().compile(\n",
|
||||
" pipeline_func=pipeline, package_path=\"dataflow_file_conversion.yaml\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"pipeline = aip.PipelineJob(\n",
|
||||
" display_name=\"dataflow_file_conversion\",\n",
|
||||
" template_path=\"dataflow_file_conversion.yaml\",\n",
|
||||
" pipeline_root=PIPELINE_ROOT,\n",
|
||||
" enable_caching=False,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"pipeline.run()\n",
|
||||
"\n",
|
||||
"! gsutil ls $GCS_CONVERT_OUT\n",
|
||||
"\n",
|
||||
"! rm -f dataflow_file_conversion.yaml gaming_schema.avsc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "delete_pipeline"
|
||||
},
|
||||
"source": [
|
||||
"### Delete a pipeline job\n",
|
||||
"\n",
|
||||
"After a pipeline job is completed, you can delete the pipeline job with the method `delete()`. Prior to completion, a pipeline job can be canceled with the method `cancel()`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "delete_pipeline"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pipeline.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"# Cleaning up\n",
|
||||
"\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",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial:\n",
|
||||
"\n",
|
||||
"- Dataset\n",
|
||||
"- Cloud Storage Bucket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Warning: Setting this to true will delete everything in your bucket\n",
|
||||
"delete_bucket = False\n",
|
||||
"\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil rm -r $BUCKET_URI"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "get_started_with_dataflow_flex_template_component.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -1333,7 +1333,6 @@
|
||||
"Next, you compile the pipeline and then exeute it. The pipeline takes the following parameters, which are passed as the dictionary `parameter_values`:\n",
|
||||
"\n",
|
||||
"- `display_name`: A human readable name for the pipeline job.\n",
|
||||
"- `import_file`: The Cloud Storage location to the dataset.\n",
|
||||
"- `worker_pool_specs`: The the machine and container, and auto-scaling requirements, as well as command line arguments.\n",
|
||||
"- `study_spec_metrics`: The metrics to optimize in the study trials.\n",
|
||||
"- `study_spec_parameters`: The parameters to tune."
|
||||
|
||||
@@ -29,22 +29,22 @@
|
||||
"id": "title:generic,gcp"
|
||||
},
|
||||
"source": [
|
||||
"# E2E ML on GCP: MLOps stage 4 : formalization: get started with Vertex ML Metadata\n",
|
||||
"# Get started with Vertex ML Metadata\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage4/get_started_with_vertex_ml_metadata.ipynb\">\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/ml_metadata/get_started_with_vertex_ml_metadata.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage4/get_started_with_vertex_ml_metadata.ipynb\">\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/official/ml_metadata/get_started_with_vertex_ml_metadata.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/ml_ops/stage4/get_started_with_vertex_ml_metadata.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/official/ml_metadata/get_started_with_vertex_ml_metadata.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",
|
||||
@@ -62,7 +62,9 @@
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This tutorial demonstrates how to use Vertex AI for E2E MLOps on Google Cloud in production. This tutorial covers stage 4 : formalization: get started with Vertex ML Metadata."
|
||||
"This tutorial demonstrates how to use Vertex ML Metadata.\n",
|
||||
"\n",
|
||||
"Learn more about [Vertex ML Metadata](https://cloud.google.com/vertex-ai/docs/ml-metadata)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -144,19 +146,32 @@
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# The Vertex AI Workbench Notebook product has specific requirements\n",
|
||||
"IS_WORKBENCH_NOTEBOOK = os.getenv(\"DL_ANACONDA_HOME\")\n",
|
||||
"IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(\n",
|
||||
" \"/opt/deeplearning/metadata/env_version\"\n",
|
||||
")\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform[tensorboard] \\\n",
|
||||
" google-cloud-pipeline-components --quiet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only: Uncomment the following cell to restart the kernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "D-ZBOjErv5mM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Automatically restart kernel after installs so that your environment can access the new packages\n",
|
||||
"# import IPython\n",
|
||||
"\n",
|
||||
"# Vertex AI Notebook requires dependencies to be installed with '--user'\n",
|
||||
"USER_FLAG = \"\"\n",
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform[tensorboard] $USER_FLAG -q\n",
|
||||
"! pip3 install --upgrade google-cloud-pipeline-components $USER_FLAG -q"
|
||||
"# app = IPython.Application.instance()\n",
|
||||
"# app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -170,63 +185,27 @@
|
||||
"Once you've installed the additional packages, you need to restart the notebook kernel so it can find the packages."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "before_you_begin"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"\n",
|
||||
"### GPU runtime\n",
|
||||
"\n",
|
||||
"*Make sure you're running this notebook in a GPU runtime if you have that option. In Colab, select* **Runtime > Change Runtime Type > GPU**\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",
|
||||
"2. [Make sure that billing is enabled for your project.](https://cloud.google.com/billing/docs/how-to/modify-project)\n",
|
||||
"\n",
|
||||
"3. [Enable the following APIs: Vertex AI APIs, Compute Engine APIs, and Cloud Storage.](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component,storage-component.googleapis.com)\n",
|
||||
"\n",
|
||||
"4. If you are running this notebook locally, you need to install the [Cloud SDK]((https://cloud.google.com/sdk)).\n",
|
||||
"\n",
|
||||
"5. 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 `$`."
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "project_id"
|
||||
"id": "before_you_begin:nogpu"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"### 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)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -237,33 +216,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_project_id"
|
||||
},
|
||||
"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"
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -274,16 +230,7 @@
|
||||
"source": [
|
||||
"#### Region\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",
|
||||
"\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)."
|
||||
"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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -294,34 +241,7 @@
|
||||
},
|
||||
"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\")"
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -332,57 +252,70 @@
|
||||
"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",
|
||||
"**Click Create service account**.\n",
|
||||
"\n",
|
||||
"In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"\n",
|
||||
"In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex\" into the filter box, and select **Vertex Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"\n",
|
||||
"Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
"Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "FvQeFm3Gv5mR"
|
||||
},
|
||||
"source": [
|
||||
"**1. Vertex AI Workbench**\n",
|
||||
"* Do nothing as you are already authenticated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ad1138a125ea"
|
||||
},
|
||||
"source": [
|
||||
"**2. Local JupyterLab instance, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "gcp_authenticate"
|
||||
"id": "ce6043da7b33"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ! gcloud auth login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0367eac06a10"
|
||||
},
|
||||
"source": [
|
||||
"**3. Colab, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "21ad4dbb4a61"
|
||||
},
|
||||
"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 ''"
|
||||
"# from google.colab import auth\n",
|
||||
"# auth.authenticate_user()\n",
|
||||
"# IS_COLAB = True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c13224697bfb"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -393,11 +326,7 @@
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"When you initialize the Vertex SDK for Python, you specify a Cloud Storage staging bucket. The staging bucket is where all the data associated with your dataset and model resources are retained across sessions.\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."
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -408,21 +337,7 @@
|
||||
},
|
||||
"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"
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -445,26 +360,6 @@
|
||||
"! 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"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -545,19 +440,7 @@
|
||||
"source": [
|
||||
"### Set up variables\n",
|
||||
"\n",
|
||||
"Next, set up some variables used throughout the tutorial.\n",
|
||||
"### Import libraries and define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "import_aip:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import google.cloud.aiplatform as aip"
|
||||
"Next, set up some variables used throughout the tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -579,7 +462,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import google.cloud.aiplatform_v1beta1 as aip_beta"
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"import google.cloud.aiplatform_v1beta1 as aip_beta\n",
|
||||
"from google.cloud import aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -707,7 +593,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metadata_store = clients[\"metadata\"].create_metadata_store(\n",
|
||||
" parent=PARENT, metadata_store_id=\"my-metadata-store\"\n",
|
||||
" parent=PARENT, metadata_store_id=\"my-metadata-store-unique\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"metadata_store_id = str(metadata_store.result())[7:-2]\n",
|
||||
@@ -1144,7 +1030,7 @@
|
||||
"source": [
|
||||
"from kfp.v2 import compiler, dsl\n",
|
||||
"from kfp.v2.dsl import (Artifact, Dataset, Input, Metrics, Model, Output,\n",
|
||||
" OutputPath, component, pipeline)"
|
||||
" OutputPath, component)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1196,7 +1082,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@component(\n",
|
||||
" packages_to_install=[\"google-cloud-bigquery\", \"pandas\", \"pyarrow\"],\n",
|
||||
" packages_to_install=[\"google-cloud-bigquery\", \"pandas\", \"pyarrow\", \"db-dtypes\"],\n",
|
||||
" base_image=\"python:3.9\",\n",
|
||||
" output_component_file=\"create_dataset.yaml\",\n",
|
||||
")\n",
|
||||
@@ -1214,7 +1100,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"@component(\n",
|
||||
" packages_to_install=[\"sklearn\", \"pandas\", \"joblib\"],\n",
|
||||
" packages_to_install=[\"scikit-learn\", \"pandas\", \"joblib\"],\n",
|
||||
" base_image=\"python:3.9\",\n",
|
||||
" output_component_file=\"beans_model_component.yaml\",\n",
|
||||
")\n",
|
||||
@@ -1298,7 +1184,7 @@
|
||||
" # A name for the pipeline.\n",
|
||||
" name=\"mlmd-pipeline\",\n",
|
||||
")\n",
|
||||
"def pipeline(\n",
|
||||
"def my_pipeline(\n",
|
||||
" bq_table: str = \"\",\n",
|
||||
" output_data_path: str = \"data.csv\",\n",
|
||||
" project: str = PROJECT_ID,\n",
|
||||
@@ -1332,20 +1218,22 @@
|
||||
"source": [
|
||||
"NOW = datetime.now().isoformat().replace(\".\", \":\")[:-7]\n",
|
||||
"\n",
|
||||
"compiler.Compiler().compile(pipeline_func=pipeline, package_path=\"mlmd_pipeline.json\")\n",
|
||||
"compiler.Compiler().compile(\n",
|
||||
" pipeline_func=my_pipeline, package_path=\"mlmd_pipeline.json\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"run1 = aip.PipelineJob(\n",
|
||||
"run1 = aiplatform.PipelineJob(\n",
|
||||
" display_name=\"mlmd-pipeline\",\n",
|
||||
" template_path=\"mlmd_pipeline.json\",\n",
|
||||
" job_id=\"mlmd-pipeline-small-{}\".format(TIMESTAMP),\n",
|
||||
" job_id=\"mlmd-pipeline-small-unique\",\n",
|
||||
" parameter_values={\"bq_table\": \"sara-vertex-demos.beans_demo.small_dataset\"},\n",
|
||||
" enable_caching=True,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"run2 = aip.PipelineJob(\n",
|
||||
"run2 = aiplatform.PipelineJob(\n",
|
||||
" display_name=\"mlmd-pipeline\",\n",
|
||||
" template_path=\"mlmd_pipeline.json\",\n",
|
||||
" job_id=\"mlmd-pipeline-large-{}\".format(TIMESTAMP),\n",
|
||||
" job_id=\"mlmd-pipeline-large-unique\",\n",
|
||||
" parameter_values={\"bq_table\": \"sara-vertex-demos.beans_demo.large_dataset\"},\n",
|
||||
" enable_caching=True,\n",
|
||||
")\n",
|
||||
@@ -1382,7 +1270,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"df = aip.get_pipeline_df(pipeline=\"mlmd-pipeline\")\n",
|
||||
"df = aiplatform.get_pipeline_df(pipeline=\"mlmd-pipeline\")\n",
|
||||
"print(df)"
|
||||
]
|
||||
},
|
||||
@@ -1466,6 +1354,10 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metadata_store_id = (\n",
|
||||
" f\"projects/{PROJECT_ID}/locations/{REGION}/metadataStores/my-metadata-store-unique\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"clients[\"metadata\"].delete_metadata_store(name=metadata_store_id)"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1028,6 +1028,9 @@
|
||||
"deployment_resource_pool.dedicated_resources.min_replica_count = MIN_NODES\n",
|
||||
"deployment_resource_pool.dedicated_resources.max_replica_count = MAX_NODES\n",
|
||||
"deployment_resource_pool.dedicated_resources.machine_spec.machine_type = DEPLOY_COMPUTE\n",
|
||||
"if DEPLOY_NGPU:\n",
|
||||
" deployment_resource_pool.dedicated_resources.machine_spec.accelerator_type = DEPLOY_GPU\n",
|
||||
" deployment_resource_pool.dedicated_resources.machine_spec.accelerator_count = DEPLOY_NGPU\n",
|
||||
"\n",
|
||||
"request = aip_beta.CreateDeploymentResourcePoolRequest(\n",
|
||||
" parent=f\"projects/{PROJECT_ID}/locations/{REGION}\",\n",
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "4dc4391f6be7"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Hugging Face Local Inference\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_huggingface_local_inference.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_huggingface_local_inference.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_huggingface_local_inference.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",
|
||||
" (a Python-3 GPU notebook with preinstalled HuggingFace/transformer libraries is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "4e8a0fdd6f44"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to run local inference with various Hugging Face models by using [Colab](https://colab.research.google.com/) and installing the necessary libraries or by deploying a [Vertex AI Workbench Instance](https://cloud.google.com/vertex-ai-workbench) with preinstalled transformer and diffuser libraries.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Run local inference with various transformer or diffusion models.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "69453bf7230e"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "68990d91bc5f"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "3d342b32fb08"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" ! pip3 install --upgrade pip\n",
|
||||
" ! pip3 install torchvision==0.14.1\n",
|
||||
" ! pip3 install transformers==4.27.1\n",
|
||||
" ! pip3 install diffusers==0.15.1\n",
|
||||
" ! apt-get update\n",
|
||||
" ! apt-get install -y --no-install-recommends tesseract-ocr\n",
|
||||
" ! pip3 install tesseract==0.1.3\n",
|
||||
" ! pip3 install pytesseract==0.3.10\n",
|
||||
" ! pip3 install datasets==2.9.0\n",
|
||||
" ! pip3 install accelerate==0.18.0\n",
|
||||
" ! pip3 install triton==2.0.0.dev20221120\n",
|
||||
" ! pip3 install xformers==0.0.16\n",
|
||||
" ! pip3 install modelscope==1.4.2\n",
|
||||
" ! pip3 install open_clip_torch==2.17.1\n",
|
||||
" ! pip3 install pytorch-lightning==1.9.5\n",
|
||||
" ! pip3 install opencv-python-headless==4.7.0.72\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
" # Remove wrong cublas version.\n",
|
||||
" ! pip3 uninstall nvidia_cublas_cu11 --yes\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "05e23144b125"
|
||||
},
|
||||
"source": [
|
||||
"### Workbench only\n",
|
||||
"\n",
|
||||
"1. Follow [this link](https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_huggingfacE_local_inference.ipynb) to deploy the notebook to a Vertex AI Workbench Instance.\n",
|
||||
"2. Select `Create a new Notebook`.\n",
|
||||
"3. Click `Advanced Options`.\n",
|
||||
"4. In the **Environment** tab, select `Debian 10` for **Operating System** and select `Custom Container` for **Environment**.\n",
|
||||
"5. Set the **Docker container image** field to `us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/transformers-notebook`.\n",
|
||||
"6. In the **Machine Type** tab, select a 1 `T4` GPU and select `Install NVIDIA GPU driver automatically for me`.\n",
|
||||
"7. Click `Create` to create the Vertex AI Workbench instance.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ad1a690839d5"
|
||||
},
|
||||
"source": [
|
||||
"## Sample code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0a4008240483"
|
||||
},
|
||||
"source": [
|
||||
"#### [runwayml/stable-diffusion-v1-5](https://huggingface.co/runwayml/stable-diffusion-v1-5) (Text-to-image)\n",
|
||||
"Generate photo-realistic images given any text input."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "5ec6b474d1be"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionPipeline\n",
|
||||
"\n",
|
||||
"model_id = \"runwayml/stable-diffusion-v1-5\"\n",
|
||||
"pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16)\n",
|
||||
"pipe = pipe.to(\"cuda\")\n",
|
||||
"\n",
|
||||
"prompt = \"a photo of an astronaut riding a horse on mars\"\n",
|
||||
"image = pipe(prompt).images[0]\n",
|
||||
"\n",
|
||||
"display(image)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ae94b9b23a52"
|
||||
},
|
||||
"source": [
|
||||
"#### [runwayml/stable-diffusion-v1-5](https://huggingface.co/runwayml/stable-diffusion-v1-5) (Text guided image-to-image)\n",
|
||||
"Generate an image based on an initial image and a text prompt."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "0acd70f4d08a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionImg2ImgPipeline\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"device = \"cuda\"\n",
|
||||
"model_id_or_path = \"runwayml/stable-diffusion-v1-5\"\n",
|
||||
"pipe = StableDiffusionImg2ImgPipeline.from_pretrained(\n",
|
||||
" model_id_or_path, torch_dtype=torch.float16\n",
|
||||
")\n",
|
||||
"pipe = pipe.to(device)\n",
|
||||
"\n",
|
||||
"url = \"https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg\"\n",
|
||||
"\n",
|
||||
"response = requests.get(url)\n",
|
||||
"init_image = Image.open(BytesIO(response.content)).convert(\"RGB\")\n",
|
||||
"init_image = init_image.resize((768, 512))\n",
|
||||
"\n",
|
||||
"prompt = \"A fantasy landscape, trending on artstation\"\n",
|
||||
"\n",
|
||||
"images = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5).images\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e76b3fe8d10c"
|
||||
},
|
||||
"source": [
|
||||
"#### [runwayml/stable-diffusion-inpainting](https://huggingface.co/runwayml/stable-diffusion-inpainting) (Image-inpainting)\n",
|
||||
"Generate an image based on an original image and prompt, only editing the areas denoted by a mask image."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "8bc3238be4e7"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionInpaintPipeline\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"image_url = \"https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png\"\n",
|
||||
"image_response = requests.get(image_url)\n",
|
||||
"init_image = Image.open(BytesIO(image_response.content)).convert(\"RGB\")\n",
|
||||
"display(init_image)\n",
|
||||
"\n",
|
||||
"mask_url = \"https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png\"\n",
|
||||
"mask_response = requests.get(mask_url)\n",
|
||||
"mask_image = Image.open(BytesIO(mask_response.content)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"pipe = StableDiffusionInpaintPipeline.from_pretrained(\n",
|
||||
" \"runwayml/stable-diffusion-inpainting\",\n",
|
||||
" revision=\"fp16\",\n",
|
||||
" torch_dtype=torch.float16,\n",
|
||||
")\n",
|
||||
"pipe.to(\"cuda\")\n",
|
||||
"\n",
|
||||
"prompt = \"Face of a yellow cat, high resolution, sitting on a park bench\"\n",
|
||||
"images = pipe(prompt=prompt, image=init_image, mask_image=mask_image).images\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1ade95a9b20e"
|
||||
},
|
||||
"source": [
|
||||
"#### [impira/layoutlm-document-qa](https://huggingface.co/impira/layoutlm-document-qa) (Document question answering)\n",
|
||||
"Answer questions about a given document."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "492d9f1de3f2"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from transformers import pipeline\n",
|
||||
"\n",
|
||||
"nlp = pipeline(\n",
|
||||
" \"document-question-answering\",\n",
|
||||
" model=\"impira/layoutlm-document-qa\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\n",
|
||||
" nlp(\n",
|
||||
" \"https://templates.invoicehome.com/invoice-template-us-neat-750px.png\",\n",
|
||||
" \"What is the invoice number?\",\n",
|
||||
" )\n",
|
||||
")\n",
|
||||
"# [{'score': 0.9943977, 'answer': 'us-001', 'start': 15, 'end': 15}]\n",
|
||||
"\n",
|
||||
"print(\n",
|
||||
" nlp(\n",
|
||||
" \"https://miro.medium.com/max/787/1*iECQRIiOGTmEFLdWkVIH2g.jpeg\",\n",
|
||||
" \"What is the purchase amount?\",\n",
|
||||
" )\n",
|
||||
")\n",
|
||||
"# [{'score': 0.9912159, 'answer': '$1,000,000,000', 'start': 97, 'end': 97}]\n",
|
||||
"\n",
|
||||
"print(\n",
|
||||
" nlp(\n",
|
||||
" \"https://www.accountingcoach.com/wp-content/uploads/2013/10/income-statement-example@2x.png\",\n",
|
||||
" \"What are the 2020 net sales?\",\n",
|
||||
" )\n",
|
||||
")\n",
|
||||
"# [{'score': 0.978011429309845, 'answer': '$ 3,980', 'start': 15, 'end': 16}]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_huggingface_local_inference.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,874 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - JAX Vision Transformer\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_jax_vision_transformer.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_jax_vision_transformer.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_jax_vision_transformer.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "24743cf4a1e1"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates finetuning a [JAX ViT-B16 model](https://github.com/google-research/vision_transformer#available-vit-models) for image classification task on GPU and deploying them on Vertex AI for online prediction."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d975e698c9a4"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn how to:\n",
|
||||
"\n",
|
||||
"- Finetune a JAX Vision Transformer based model.\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image classification.\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services and resources:\n",
|
||||
"\n",
|
||||
"- Vertex AI Training\n",
|
||||
"- Vertex AI Model Registry\n",
|
||||
"- Vertex AI Online Prediction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "08d289fa873f"
|
||||
},
|
||||
"source": [
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"This notebook uses the [tf_flowers dataset](https://www.tensorflow.org/datasets/catalog/tf_flowers) and has a section which shows how to download and prepare it. You can follow similar process to use your own custom dataset too."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aed92deeb4a0"
|
||||
},
|
||||
"source": [
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "i7EUnXsZhAGF"
|
||||
},
|
||||
"source": [
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"Install the following packages required to execute this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2b4ef9b72d43"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Install the packages.\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "58707a750154"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "f200f10a1da3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Automatically restart kernel after installs so that your environment can access the new packages.\n",
|
||||
"import IPython\n",
|
||||
"\n",
|
||||
"app = IPython.Application.instance()\n",
|
||||
"app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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 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 need to install the [Cloud SDK](https://cloud.google.com/sdk).\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**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"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"your-project-id\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "twgKk-LsLmX3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "sBCra4QMA2wR"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "74ccc9e52986"
|
||||
},
|
||||
"source": [
|
||||
"**1. Vertex AI Workbench**\n",
|
||||
"* Do nothing as you are already authenticated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "de775a3773ba"
|
||||
},
|
||||
"source": [
|
||||
"**2. Local JupyterLab instance, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "254614fa0c46"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ! gcloud auth login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ef21552ccea8"
|
||||
},
|
||||
"source": [
|
||||
"**3. Colab, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "603adbbf0532"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# from google.colab import auth\n",
|
||||
"# auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f6b2ccc891ed"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "MzGDU7TWdts_"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-EcIXiGsCePi"
|
||||
},
|
||||
"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": "NIq7R4HZCfIc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l {REGION} -p {PROJECT_ID} {BUCKET_URI}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "960505627ddf"
|
||||
},
|
||||
"source": [
|
||||
"### Import libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "PyQmSRbKA8r-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import glob\n",
|
||||
"import os\n",
|
||||
"import random\n",
|
||||
"import shutil\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from google.protobuf import json_format\n",
|
||||
"from google.protobuf.struct_pb2 import Value\n",
|
||||
"from PIL import Image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vS1hQiGuLmX4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"staging_bucket = os.path.join(BUCKET_URI, \"jax_vit_staging\")\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=staging_bucket)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training docker image.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/jax-vit-train-gpu\"\n",
|
||||
"# The pre-built TF SavedModel conversion docker image.\n",
|
||||
"MODEL_CONVERSION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/jax-vit-model-conversion\"\n",
|
||||
"# The pre-built prediction docker image.\n",
|
||||
"OPTIMIZED_TF_RUNTIME_IMAGE_URI = (\n",
|
||||
" \"us-docker.pkg.dev/vertex-ai-restricted/prediction/tf_opt-gpu.nightly:latest\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions\n",
|
||||
"\n",
|
||||
"This section defines functions for:\n",
|
||||
"\n",
|
||||
"- Splitting the [tf_flowers dataset](https://www.tensorflow.org/datasets/catalog/tf_flowers) images into `train` and `test` folders.\n",
|
||||
"- Converting a Cloud Storage path such as `gs://bucket-name` to GCSFuse path format such as `/gcsfuse/bucket-name`.\n",
|
||||
"- Encoding a local image file to a string for prediction input."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def split(base_dir, test_ratio=0.1):\n",
|
||||
" \"\"\"Splits images and moves them to train and test folders.\"\"\"\n",
|
||||
" paths = glob.glob(f\"{base_dir}/*/*.jpg\")\n",
|
||||
" random.shuffle(paths)\n",
|
||||
" counts = dict(test=0, train=0)\n",
|
||||
" for i, path in enumerate(paths):\n",
|
||||
" split = \"test\" if i < test_ratio * len(paths) else \"train\"\n",
|
||||
" *_, class_name, basename = path.split(\"/\")\n",
|
||||
" dst = f\"{base_dir}/{split}/{class_name}/{basename}\"\n",
|
||||
" if not os.path.isdir(os.path.dirname(dst)):\n",
|
||||
" os.makedirs(os.path.dirname(dst))\n",
|
||||
" shutil.move(path, dst)\n",
|
||||
" counts[split] += 1\n",
|
||||
" print(f'Moved {counts[\"train\"]:,} train and {counts[\"test\"]:,} test images.')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def gcs_fuse_path(path: str) -> str:\n",
|
||||
" \"\"\"Try to convert path to gcsfuse path if it starts with gs:// else do not modify it.\"\"\"\n",
|
||||
" path = path.strip()\n",
|
||||
" if path.startswith(\"gs://\"):\n",
|
||||
" return \"/gcs/\" + path[5:]\n",
|
||||
" return path\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_bytes_from_local_image(local_image_path, new_width=-1):\n",
|
||||
" \"\"\"Returns encoded image string for prediction input.\"\"\"\n",
|
||||
" image = Image.open(local_image_path)\n",
|
||||
" if new_width <= 0:\n",
|
||||
" new_image = image\n",
|
||||
" else:\n",
|
||||
" width, height = image.size\n",
|
||||
" print(\"original input image size: \", width, \" , \", height)\n",
|
||||
" new_height = int(height * new_width / width)\n",
|
||||
" print(\"new input image size: \", new_width, \" , \", new_height)\n",
|
||||
" new_image = image.resize((new_width, new_height))\n",
|
||||
" buffered = BytesIO()\n",
|
||||
" new_image.save(buffered, format=\"JPEG\")\n",
|
||||
" encoded_string = base64.b64encode(buffered.getvalue()).decode(\"utf-8\")\n",
|
||||
" return encoded_string"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "X0wWrfhDC8ni"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare dataset\n",
|
||||
"\n",
|
||||
"If you are not using [TensorFlow Datasets](https://www.tensorflow.org/datasets/catalog/overview#all_datasets), then you need to prepare your dataset and store it on Cloud Storage. The following example shows\n",
|
||||
"how to do this for the [tf_flowers dataset](https://www.tensorflow.org/datasets/catalog/tf_flowers). If using TensorFlow Datasets, you can just pass\n",
|
||||
"the dataset name such as `tf_flowers` to the `--config.dataset` flag and bypass this section."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "LW31Ws1RN9AC"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"local_flower_data_directory = \"./flower_photos\" # @param {type:\"string\"}\n",
|
||||
"FLOWER_DATA_GCS_PATH = os.path.join(BUCKET_URI, \"flower_dataset\")\n",
|
||||
"# The flower dataset has 5 classes.\n",
|
||||
"NUM_CLASSES = 5\n",
|
||||
"# NOTE: For custom dataset, the training code picks the class names\n",
|
||||
"# from the folder structure and then sorts them to create a mapping\n",
|
||||
"# from class-index to class-name. This is why the mapping below\n",
|
||||
"# looks different from default `tf_flowers` documentation.\n",
|
||||
"LABEL_IDX_TO_STR = {\n",
|
||||
" 0: \"daisy\",\n",
|
||||
" 1: \"dandelion\",\n",
|
||||
" 2: \"roses\",\n",
|
||||
" 3: \"sunflowers\",\n",
|
||||
" 4: \"tulips\",\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "heMhYO-DD4II"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Download flower data to a local directory.\n",
|
||||
"! rm -rf $local_flower_data_directory;\n",
|
||||
"! (cd \"./\" && curl https://storage.googleapis.com/download.tensorflow.org/example_images/flower_photos.tgz | tar xz)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "YtWxe2y8Gqzl"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Since the default file format of above \"tf_flowers\" dataset is\n",
|
||||
"# flower_photos/{class_name}/{filename}.jpg\n",
|
||||
"# we first need to split it into a \"train\" (90%) and a \"test\" (10%) set:\n",
|
||||
"# flower_photos/train/{class_name}/{filename}.jpg\n",
|
||||
"# flower_photos/test/{class_name}/{filename}.jpg\n",
|
||||
"\n",
|
||||
"split(local_flower_data_directory)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "g043ydQ_wlpk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Move Flower data from local directory to Cloud Storage.\n",
|
||||
"# This step takes around 2 mins to finish.\n",
|
||||
"! gsutil -m cp -R $local_flower_data_directory/train/* $FLOWER_DATA_GCS_PATH/train/\n",
|
||||
"! gsutil -m cp -R $local_flower_data_directory/test/* $FLOWER_DATA_GCS_PATH/test/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aCpLmWPMpJQ8"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune with JAX Vision Transformer\n",
|
||||
"\n",
|
||||
"Create and run the training job with the model-garden JAX vision transformer training docker using the Vertex AI SDK. The training uses one V100 GPU and runs for around 10 mins once the training job begins."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set up training docker arguments.\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
"JOB_NAME = \"jax_vision_transformer\" + TIMESTAMP\n",
|
||||
"\n",
|
||||
"finetuning_workdir = os.path.join(BUCKET_URI, JOB_NAME)\n",
|
||||
"pre_trained_dir = \"gs://vit_models/imagenet21k\"\n",
|
||||
"docker_args_list = [\n",
|
||||
" \"--config\",\n",
|
||||
" \"vit_jax/configs/vit.py:b16\",\n",
|
||||
" \"--config.dataset\",\n",
|
||||
" f\"{gcs_fuse_path(FLOWER_DATA_GCS_PATH)}\",\n",
|
||||
" \"--config.pp.train\",\n",
|
||||
" \"train\",\n",
|
||||
" \"--config.pp.test\",\n",
|
||||
" \"test\",\n",
|
||||
" \"--config.pretrained_dir\",\n",
|
||||
" f\"{gcs_fuse_path(pre_trained_dir)}\",\n",
|
||||
" \"--config.batch\",\n",
|
||||
" \"128\",\n",
|
||||
" \"--config.batch_eval\",\n",
|
||||
" \"128\",\n",
|
||||
" \"--config.base_lr\",\n",
|
||||
" \"0.01\",\n",
|
||||
" \"--config.shuffle_buffer\",\n",
|
||||
" \"1000\",\n",
|
||||
" \"--config.total_steps\",\n",
|
||||
" \"100\",\n",
|
||||
" \"--config.warmup_steps\",\n",
|
||||
" \"10\",\n",
|
||||
" \"--config.pp.crop\",\n",
|
||||
" \"224\",\n",
|
||||
" \"--workdir\",\n",
|
||||
" f\"{gcs_fuse_path(finetuning_workdir)}\",\n",
|
||||
"]\n",
|
||||
"print(docker_args_list)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ELphfgj1f3Q"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create and run the training job.\n",
|
||||
"# Click on the generated link in the output under \"View backing custom job:\" to see your run in the Cloud Console.\n",
|
||||
"NUM_GPU = 1\n",
|
||||
"container_uri = TRAIN_DOCKER_URI\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=JOB_NAME,\n",
|
||||
" container_uri=container_uri,\n",
|
||||
")\n",
|
||||
"model = job.run(\n",
|
||||
" args=docker_args_list,\n",
|
||||
" base_output_dir=f\"{finetuning_workdir}\",\n",
|
||||
" replica_count=1,\n",
|
||||
" machine_type=\"n1-standard-4\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=NUM_GPU,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-2qiROKIONnI"
|
||||
},
|
||||
"source": [
|
||||
"## Convert JAX Vision Transformer model to TF SavedModel\n",
|
||||
"\n",
|
||||
"Convert the previously fine-tuned JAX model to a TF SavedModel for online prediction."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6Y7slAFLOwlV"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set up model conversion docker arguments.\n",
|
||||
"# Note: Many of the arguments below are similar to the training job\n",
|
||||
"# such as the model name and train and test data related parameters.\n",
|
||||
"\n",
|
||||
"jax_checkpoint_dir = finetuning_workdir\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
"JOB_NAME = \"jax_model_conversion\" + TIMESTAMP\n",
|
||||
"saved_model_dir = os.path.join(BUCKET_URI, \"jax2tf_\" + TIMESTAMP)\n",
|
||||
"\n",
|
||||
"docker_args_list = [\n",
|
||||
" \"--config\",\n",
|
||||
" \"vit_jax/configs/vit.py:b16\",\n",
|
||||
" \"--num_classes\",\n",
|
||||
" f\"{NUM_CLASSES}\",\n",
|
||||
" \"--saved_model_dir\",\n",
|
||||
" f\"{saved_model_dir}\",\n",
|
||||
" \"--jax_checkpoint_dir\",\n",
|
||||
" f\"{jax_checkpoint_dir}\",\n",
|
||||
" \"--config.pretrained_dir\",\n",
|
||||
" f\"{pre_trained_dir}\",\n",
|
||||
" \"--config.dataset\",\n",
|
||||
" f\"{gcs_fuse_path(FLOWER_DATA_GCS_PATH)}\",\n",
|
||||
" \"--config.pp.train\",\n",
|
||||
" \"train\",\n",
|
||||
" \"--config.pp.test\",\n",
|
||||
" \"test\",\n",
|
||||
" \"--config.pp.crop\",\n",
|
||||
" \"224\",\n",
|
||||
"]\n",
|
||||
"print(docker_args_list)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "0Acfh1VWUsTL"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create and run the model conversion job.\n",
|
||||
"# Click on the generated link in the output under \"View backing custom job:\" to see your run in the Cloud Console.\n",
|
||||
"container_uri = MODEL_CONVERSION_DOCKER_URI\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=JOB_NAME,\n",
|
||||
" container_uri=container_uri,\n",
|
||||
")\n",
|
||||
"model_conversion_workdir = os.path.join(BUCKET_URI, JOB_NAME)\n",
|
||||
"model = job.run(\n",
|
||||
" args=docker_args_list,\n",
|
||||
" base_output_dir=f\"{model_conversion_workdir}\",\n",
|
||||
" replica_count=1,\n",
|
||||
" machine_type=\"n1-standard-4\",\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "iILhhP3TfO8B"
|
||||
},
|
||||
"source": [
|
||||
"## Run online prediction\n",
|
||||
"\n",
|
||||
"Run online prediction with the converted TF SavedModel."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "XswgX6JqRwFK"
|
||||
},
|
||||
"source": [
|
||||
"Upload TF SavedModel and deploy it to an endpoint for prediction. This step takes around 15 minutes to finish."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "74yqis5ufO8B"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"jax_vit_model = aiplatform.Model.upload(\n",
|
||||
" display_name=\"jax_vit\",\n",
|
||||
" artifact_uri=saved_model_dir,\n",
|
||||
" serving_container_image_uri=OPTIMIZED_TF_RUNTIME_IMAGE_URI,\n",
|
||||
" serving_container_args=[],\n",
|
||||
" location=REGION,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"jax_vit_endpoint = jax_vit_model.deploy(\n",
|
||||
" deployed_model_display_name=\"jax_vit_deployed\",\n",
|
||||
" traffic_split={\"0\": 100},\n",
|
||||
" machine_type=\"n1-standard-4\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" min_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "iiozz1aVR7Pe"
|
||||
},
|
||||
"source": [
|
||||
"Load a local test image file, encode it into a string, send it to the endpoint for prediction, and then generate the final class label from the predicted class probabilities."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "qxj4Xv_DhHXj"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"test_directory = os.path.join(local_flower_data_directory, \"test/tulips\")\n",
|
||||
"local_test_image_path = os.path.join(test_directory, os.listdir(test_directory)[0])\n",
|
||||
"print(local_test_image_path)\n",
|
||||
"instances_list = [\n",
|
||||
" {\n",
|
||||
" \"bytes_inputs\": {\n",
|
||||
" \"b64\": load_bytes_from_local_image(local_test_image_path, new_width=240)\n",
|
||||
" }\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"instances = [json_format.ParseDict(s, Value()) for s in instances_list]\n",
|
||||
"results = jax_vit_endpoint.predict(instances=instances)\n",
|
||||
"logits = results.predictions[0]\n",
|
||||
"predicted_label = LABEL_IDX_TO_STR[int(np.argmax(logits))]\n",
|
||||
"print(\"predicted_label: \", predicted_label)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TpV-iwP9qw9c"
|
||||
},
|
||||
"source": [
|
||||
"## Cleaning up\n",
|
||||
"\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",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "sx_vKniMq9ZX"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete endpoint resource.\n",
|
||||
"jax_vit_endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete model resource.\n",
|
||||
"jax_vit_model.delete()\n",
|
||||
"\n",
|
||||
"# Delete Cloud Storage objects that were created.\n",
|
||||
"delete_bucket = False\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil -m rm -r $BUCKET_URI"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_jax_vision_transformer.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,745 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden Keras Stable Diffusion\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_keras_stable_diffusion.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_keras_stable_diffusion.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_keras_stable_diffusion.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9\n",
|
||||
"\n",
|
||||
"You can open this notebook directly in colab, or create [google managed](https://cloud.google.com/vertex-ai/docs/workbench/managed/create-instance) or [user managed](https://cloud.google.com/vertex-ai/docs/workbench/user-managed/create-new) workbench instances."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [Keras Stable Diffusion](https://keras.io/api/keras_cv/models/stable_diffusion) in Vertex AI Model Garden."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0z9r_mBmDeYh"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Run local inferences for pretrained or customized models\n",
|
||||
"\n",
|
||||
"* Deploy pretrained or customized models in Google Cloud Vertex AI\n",
|
||||
"\n",
|
||||
"* Finetune models in Google Cloud Vertex AI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "xxo28lDtDxn-"
|
||||
},
|
||||
"source": [
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"We use the dataset\n",
|
||||
"[Pokémon BLIP captions](https://huggingface.co/datasets/lambdalabs/pokemon-blip-captions) to show how to finetune the stable diffusion models.\n",
|
||||
"However, we'll use a slightly different version which was derived from the original\n",
|
||||
"dataset to fit better with `tf.data`. Refer to\n",
|
||||
"[the documentation](https://huggingface.co/datasets/sayakpaul/pokemon-blip-original-version)\n",
|
||||
"for more details."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "AEnkHABrDijz"
|
||||
},
|
||||
"source": [
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"## Installation\n",
|
||||
"\n",
|
||||
"Install the following packages required to execute this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" # Configs for colab notebooks.\n",
|
||||
" ! pip3 install --upgrade --quiet google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
"# Configs for all notebooks.\n",
|
||||
"! pip3 install --quiet keras-cv==0.4.1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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 API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\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. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint.\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": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"### Set your project, region and buckets\n",
|
||||
"\n",
|
||||
"**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)\n",
|
||||
"\n",
|
||||
"You can change the `REGION` variable used by Vertex AI. Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations).\n",
|
||||
"\n",
|
||||
"You can create a storage bucket to store intermediate artifacts such as datasets, trained models etc."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "YjNCFxq0JxlA"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The project and bucket are for experiments below.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"# The form for BUCKET_URI is gs://<bucket-name>.\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"keras\")\n",
|
||||
"DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
|
||||
"MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "uDjp76aaLZY9"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "5uv7-iDKLbO0"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZZFPe_GezXg8"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants and common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"from google.cloud import storage\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"GCS_URI_PREFIX = \"gs://\"\n",
|
||||
"\n",
|
||||
"# Training constants.\n",
|
||||
"TRAINING_JOB_PREFIX = \"train\"\n",
|
||||
"TRAIN_CONTAINER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/keras-train:latest\"\n",
|
||||
"TRAIN_MACHINE_TYPE = \"a2-highgpu-1g\"\n",
|
||||
"TRAIN_ACCELERATOR_TYPE = \"NVIDIA_TESLA_A100\"\n",
|
||||
"TRAIN_NUM_GPU = 1\n",
|
||||
"RESOLUTION = 512\n",
|
||||
"\n",
|
||||
"# Prediction constants.\n",
|
||||
"PREDICTION_CONTAINER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/keras-serve:latest\"\n",
|
||||
"PREDICTION_ACCELERATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"PREDICTION_MACHINE_TYPE = \"n1-standard-8\"\n",
|
||||
"DEPLOY_JOB_PREFIX = \"deploy\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_data_to_gcs(tar_filepath, gcs_bucket):\n",
|
||||
" filename_with_ext = os.path.basename(tar_filepath)\n",
|
||||
" filename_without_ext = filename_with_ext.replace(\".tar.gz\", \"\")\n",
|
||||
" print(\"Download files from: \", tar_filepath)\n",
|
||||
" ! wget $tar_filepath -O $filename_with_ext\n",
|
||||
" ! mkdir -p $filename_without_ext\n",
|
||||
" ! tar -xvf $filename_with_ext -C .\n",
|
||||
"\n",
|
||||
" ! gsutil -m cp -r $filename_without_ext $gcs_bucket/\n",
|
||||
" gcs_path = os.path.join(gcs_bucket, filename_without_ext)\n",
|
||||
" print(\"Upload files to: \", gcs_path)\n",
|
||||
" return gcs_path\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_gcs_file_to_local(gcs_uri: str, local_path: str):\n",
|
||||
" \"\"\"Download a gcs file to a local path.\n",
|
||||
"\n",
|
||||
" Args:\n",
|
||||
" gcs_uri: A string of file path on GCS.\n",
|
||||
" local_path: A string of local file path.\n",
|
||||
" \"\"\"\n",
|
||||
" if not gcs_uri.startswith(GCS_URI_PREFIX):\n",
|
||||
" raise ValueError(f\"{gcs_uri} is not a GCS path starting with {GCS_URI_PREFIX}.\")\n",
|
||||
" client = storage.Client()\n",
|
||||
" os.makedirs(os.path.dirname(local_path), exist_ok=True)\n",
|
||||
" with open(local_path, \"wb\") as f:\n",
|
||||
" client.download_blob_to_file(gcs_uri, f)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_path, service_account):\n",
|
||||
"\n",
|
||||
" deploy_model_name = get_job_name_with_datetime(DEPLOY_JOB_PREFIX)\n",
|
||||
" print(\"The deployed job name is: \", deploy_model_name)\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_PATH\": f\"{model_path}\",\n",
|
||||
" \"IMAGE_WIDTH\": f\"{RESOLUTION}\",\n",
|
||||
" \"IMAGE_HEIGHT\": f\"{RESOLUTION}\",\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{deploy_model_name}-endpoint\")\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=deploy_model_name,\n",
|
||||
" serving_container_image_uri=PREDICTION_CONTAINER_URI,\n",
|
||||
" serving_container_ports=[8501],\n",
|
||||
" serving_container_predict_route=\"/predict\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=PREDICTION_MACHINE_TYPE,\n",
|
||||
" accelerator_type=PREDICTION_ACCELERATOR_TYPE,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" min_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=service_account,\n",
|
||||
" )\n",
|
||||
" return model, endpoint\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def display_image(image):\n",
|
||||
" _ = plt.figure(figsize=(20, 15))\n",
|
||||
" plt.grid(False)\n",
|
||||
" plt.imshow(image)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def display_image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "epo-RHXzcBBT"
|
||||
},
|
||||
"source": [
|
||||
"## Run inferences\n",
|
||||
"\n",
|
||||
"This section shows how to run inferences with Keras Stable Diffusion models.\n",
|
||||
"\n",
|
||||
"1. Run inferences locally\n",
|
||||
"2. Run inferences with serving dockers\n",
|
||||
"\n",
|
||||
"You can run inferences with pre-trained models from Keras team, or your own finetuned models.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6zsa9vnBHhvO"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Sets the model_path to empty to load the pre-trained model from Keras team.\n",
|
||||
"# Sets the model_path to a gcs uri to load the finetuned models.\n",
|
||||
"model_path = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ld39hkcIceE2"
|
||||
},
|
||||
"source": [
|
||||
"### Run inferences locally\n",
|
||||
"Local inferences can finish in seconds with GPUs.\n",
|
||||
"\n",
|
||||
"Load models first."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "G1nCKVSac3Y5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from keras_cv.models import StableDiffusion\n",
|
||||
"\n",
|
||||
"model = StableDiffusion(img_height=RESOLUTION, img_width=RESOLUTION, jit_compile=True)\n",
|
||||
"if model_path.startswith(GCS_URI_PREFIX):\n",
|
||||
" local_model_path = \"/tmp/saved_model.h5\"\n",
|
||||
" download_gcs_file_to_local(model_path, local_model_path)\n",
|
||||
" model.diffusion_model.load_weights(local_model_path)\n",
|
||||
"elif model_path:\n",
|
||||
" model.diffusion_model.load_weights(model_path)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ABaCSIWuP-_G"
|
||||
},
|
||||
"source": [
|
||||
"Then run inferences."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "pnyeVsh8RNI5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"batch_size = 1\n",
|
||||
"img = model.text_to_image(\n",
|
||||
" prompt=\"a squirrel in Picasso style\",\n",
|
||||
" batch_size=batch_size, # How many images to generate at once\n",
|
||||
" num_steps=25, # Number of iterations (controls image quality)\n",
|
||||
" seed=123, # A fixed seed guarantees the same prompt always generates the same image\n",
|
||||
")\n",
|
||||
"for i in range(batch_size):\n",
|
||||
" display_image(img[i])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kY87SU9Adq4o"
|
||||
},
|
||||
"source": [
|
||||
"### Serve models with dockers\n",
|
||||
"When serve models with dockers, we will deploy models in Google Cloud Vertex AI. The default setting will use 1 V100 GPU for deployment.\n",
|
||||
"\n",
|
||||
"Please create a Service Account for serving with dockers if you do not have one yet.\n",
|
||||
"\n",
|
||||
"The model deployment will take ~10 minutes to finish."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "yCB9vu7RenY6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The service account looks like:\n",
|
||||
"# '<account_name>@<project>.iam.gserviceaccount.com'\n",
|
||||
"# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
|
||||
"# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_path=model_path,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"print(\"endpoint id is: \", endpoint_id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "72_BW_BgfvYT"
|
||||
},
|
||||
"source": [
|
||||
"Once deployed, you can send a batch of text prompts to the endpoint to generate images.\n",
|
||||
"\n",
|
||||
"Note, the inference time for the first request for a fresh deployment will need more time to process and take ~45 seconds on one V100 GPU. The inferences for further request is ~12 seconds on one V100 GPU per image."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "U_jrNcZ5eVbH"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_id = <An Existing Endpoint ID>\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_id}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"a squirrel in Picasso style\"},\n",
|
||||
" {\"prompt\": \"a dog in Picasso style\"},\n",
|
||||
" {\"prompt\": \"a cat in Picasso style\"},\n",
|
||||
" {\"prompt\": \"a deer in Picasso style\"},\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"parameters = {\n",
|
||||
" \"batch_size\": 1, # How many images to generate at once\n",
|
||||
" \"num_steps\": 25, # Number of iterations (controls image quality)\n",
|
||||
" \"seed\": 123, # A fixed seed guarantees the same prompt always generates the same image\n",
|
||||
"}\n",
|
||||
"response = endpoint.predict(instances=instances, parameters=parameters)\n",
|
||||
"# prediction['predicted_image'] will contains the prediction images in a batch.\n",
|
||||
"# The batch size in this example is 1, and the visualization only parses the\n",
|
||||
"# first predicted image.\n",
|
||||
"images = [\n",
|
||||
" base64_to_image(prediction[\"predicted_image\"][0])\n",
|
||||
" for prediction in response.predictions\n",
|
||||
"]\n",
|
||||
"display_image_grid(images, rows=2, cols=2)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "LiQF7fm6f842"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "eqJyypt-f9K6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploys models and deletes endpoints.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"# Deletes models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RB_xY9ipr7ZU"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune models\n",
|
||||
"This section shows how to finetune Keras Stable diffusion models with training dockers.\n",
|
||||
"\n",
|
||||
"If you would like to use finetuned models, please go to the section `Run inferences`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "OD3TtaWs5b4v"
|
||||
},
|
||||
"source": [
|
||||
"### Download data\n",
|
||||
" We download the data to GCS storage for the experiments with training dockers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2TVB8MU-5i-q"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Skips this step if you have already downloaded the dataset.\n",
|
||||
"download_data_to_gcs(\n",
|
||||
" \"https://huggingface.co/datasets/sayakpaul/pokemon-blip-original-version/resolve/main/pokemon_dataset.tar.gz\",\n",
|
||||
" DATA_BUCKET,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Ee7Hzq8O5jgF"
|
||||
},
|
||||
"source": [
|
||||
"### Start training jobs\n",
|
||||
"We finetune 512*512 stable diffusion models with 1 epoch and it takes ~15 minutes to finish using 1 A100 GPU with default settings."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"data_csv = os.path.join(DATA_BUCKET, \"pokemon_dataset/data.csv\")\n",
|
||||
"epochs = 1\n",
|
||||
"\n",
|
||||
"train_job_name = get_job_name_with_datetime(TRAINING_JOB_PREFIX)\n",
|
||||
"model_dir = os.path.join(MODEL_BUCKET, train_job_name)\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAIN_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAIN_ACCELERATOR_TYPE,\n",
|
||||
" \"accelerator_count\": TRAIN_NUM_GPU,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"disk_spec\": {\n",
|
||||
" \"boot_disk_type\": \"pd-ssd\",\n",
|
||||
" \"boot_disk_size_gb\": 500,\n",
|
||||
" },\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAIN_CONTAINER_URI,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"env\": [\n",
|
||||
" {\n",
|
||||
" \"name\": \"RESOLUTION\",\n",
|
||||
" \"value\": f\"{RESOLUTION}\",\n",
|
||||
" },\n",
|
||||
" ],\n",
|
||||
" \"args\": [\n",
|
||||
" f\"--epochs={epochs}\",\n",
|
||||
" f\"--input_csv_path={data_csv}\",\n",
|
||||
" f\"--output_model_dir={model_dir}\",\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"train_job = aiplatform.CustomJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_job.run()\n",
|
||||
"\n",
|
||||
"model_path = os.path.join(model_dir, \"saved_model.h5\")\n",
|
||||
"print(\"The trained model is saved as: \", model_path)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "wBlQ6FQlJhBi"
|
||||
},
|
||||
"source": [
|
||||
"After the training finishes, you can use `model_path` and then go to the `Run inferences` section above to run predictions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"train_job.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1dijQDiZWegt"
|
||||
},
|
||||
"source": [
|
||||
"## References\n",
|
||||
"\n",
|
||||
"- [Fine-tuning Stable Diffusion](https://keras.io/examples/generative/finetune_stable_diffusion/)\n",
|
||||
"- [StableDiffusion image-generation model](https://keras.io/api/keras_cv/models/stable_diffusion/)\n",
|
||||
"- [High-performance image generation using Stable Diffusion in KerasCV](https://keras.io/guides/keras_cv/generate_images_with_stable_diffusion/)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"name": "model_garden_keras_stable_diffusion.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden MediaPipe with gesture recognition\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_gesture_recognition.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_gesture_recognition.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> <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_mediapipe_gesture_recognition.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [MediaPipe Model Maker](https://developers.google.com/mediapipe/solutions/model_maker) to train an on-device gesture recognition model in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [custom jobs](https://cloud.google.com/vertex-ai/docs/training/create-custom-job) to train new models\n",
|
||||
" * Export models\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands to install dependencies and to authenticate with Google Cloud if running on Colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install --upgrade pip\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, 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"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "tTy1gX11kCJY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "MzGDU7TWdts_"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-EcIXiGsCePi"
|
||||
},
|
||||
"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": "NIq7R4HZCfIc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l {REGION} -p {PROJECT_ID} {BUCKET_URI}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "960505627ddf"
|
||||
},
|
||||
"source": [
|
||||
"### Import libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "PyQmSRbKA8r-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"import tensorflow\n",
|
||||
"from google.cloud import aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"now = datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temp/%s\" % now)\n",
|
||||
"\n",
|
||||
"EVALUATION_RESULT_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"evaluation\")\n",
|
||||
"EVALUATION_RESULT_OUTPUT_FILE = os.path.join(\n",
|
||||
" EVALUATION_RESULT_OUTPUT_DIRECTORY, \"evaluation.json\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"EXPORTED_MODEL_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"model\")\n",
|
||||
"EXPORTED_MODEL_OUTPUT_FILE = os.path.join(\n",
|
||||
" EXPORTED_MODEL_OUTPUT_DIRECTORY, \"gesture_recognizer.task\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define training machine specs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"TRAINING_JOB_DISPLAY_NAME = \"mediapipe_gesture_recognizer_%s\" % now\n",
|
||||
"TRAINING_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/mediapipe-train\"\n",
|
||||
"TRAINING_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAINING_ACCELARATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"TRAINING_ACCELERATOR_COUNT = 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-rsdAcBV-vlf"
|
||||
},
|
||||
"source": [
|
||||
"## Train your customized models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare input data for training\n",
|
||||
"\n",
|
||||
"Finetuning a model for gesture recognition requires a dataset with a directory structure following the pattern `<dataset_path>/<label_name>/<img_name>.*` (e.g. `my_custom_dataset/thumbs_up/img12.jpg`). In addition, one of the label names must be none. The none label represents any gesture that isn't classified as one of the other gestures.\n",
|
||||
"\n",
|
||||
"This example uses a rock paper scissors dataset sample which is available on Cloud Storage.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"training_data_path = (\n",
|
||||
" \"gs://mediapipe-tasks/gesture_recognizer/rps_data_sample\" # @param {type:\"string\"}\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ci4IV6vdXRMD"
|
||||
},
|
||||
"source": [
|
||||
"When Model Maker loads the dataset, it runs the pre-packaged hand detection model from MediaPipe Hands to detect the hand landmarks from the images. Any images without detected hands are ommitted from the dataset. The resulting dataset will contain the extracted hand landmark positions from each image, rather than images themselves.\n",
|
||||
"\n",
|
||||
"You can configure a few options that determine how the dataset is loaded:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aNHLSyFtXP7I"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# A boolean controlling whether to shuffle the dataset. Defaults to true.\n",
|
||||
"shuffle = True # @param {type:\"boolean\"}\n",
|
||||
"# A float between 0 and 1 controlling the confidence threshold for hand detection\n",
|
||||
"min_detection_confidence = 0.6 # @param {type:\"number\"}\n",
|
||||
"# Configures how to split the dataset between training, validation and test data. Must sum to up 1.\n",
|
||||
"split_ratio = \"0.8,0.1,0.1\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"### Set fine-tuning options\n",
|
||||
"\n",
|
||||
"You can customize the model using the by specifying ModelOptions and HParams. The ModelOptions contain parameters related to the model itself, while the HParams contains parameters related to training and saving the model.\n",
|
||||
"\n",
|
||||
"The ModelOptions contain these customizable parameter that affects accuracy:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bDxsEaoGcibW"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The fraction of the input units to drop. Used in dropout layer.\n",
|
||||
"dropout_rate: float = 0.05 # @param {type:\"number\"}\n",
|
||||
"# A list of hidden layer widths for the gesture model. Each element\n",
|
||||
"# in the list will create a new hidden layer with the specified width.\n",
|
||||
"# The hidden layers are separated with BatchNorm, Dropout, and ReLU.\n",
|
||||
"layer_widths: str = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fk0TTZbDdJPX"
|
||||
},
|
||||
"source": [
|
||||
"HParams has the following list of customizable parameters which affect model accuracy:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The learning rate to use for gradient descent training.\n",
|
||||
"learning_rate: float = 0.001 # @param {type:\"number\"}\n",
|
||||
"# Batch size for training.\n",
|
||||
"batch_size: int = 2 # @param {type:\"number\"}\n",
|
||||
"# Number of training iterations over the dataset.\n",
|
||||
"epochs: int = 10 # @param {type:\"slider\", min:0, max:100, step:1}\n",
|
||||
"# An optional integer that indicates the number of training steps per\n",
|
||||
"# epoch. If set to 0, the training pipeline calculates the default\n",
|
||||
"# steps per epoch as the training dataset size divided by batch size.\n",
|
||||
"steps_per_epoch: int = 0 # @param {type:\"number\"}\n",
|
||||
"# Whether to shuffle the dataset before training\n",
|
||||
"shuffle: bool = False # @param {type:\"boolean\"}\n",
|
||||
"# Learning rate decay to use for gradient descent training.\n",
|
||||
"lr_decay: float = 0.99 # @param {type:\"number\"}\n",
|
||||
"# Gamma parameter for focal loss. Defaults to 2\n",
|
||||
"gamma: float = 2 # @param {type:\"number\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"### Run fine-tuning\n",
|
||||
"With your training dataset and fine-tuning options prepared, you are ready to start the fine-tuning process. This process is resource intensive and can take a few minutes to complete. On Vertex AI with GPU processing, the example fine-tuning below takes between 1-2 minutes to train on approximately 500 images.\n",
|
||||
"\n",
|
||||
"To begin the fine-tuning process, use the following code:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_export_path = EXPORTED_MODEL_OUTPUT_DIRECTORY\n",
|
||||
"evaluation_result_path = EVALUATION_RESULT_OUTPUT_DIRECTORY\n",
|
||||
"\n",
|
||||
"model_options = {\"dropout_rate\": dropout_rate}\n",
|
||||
"if layer_widths:\n",
|
||||
" model_options[\"layer_widths\"] = layer_widths\n",
|
||||
"\n",
|
||||
"hparams = {\n",
|
||||
" \"learning_rate\": learning_rate,\n",
|
||||
" \"batch_size\": batch_size,\n",
|
||||
" \"epochs\": epochs,\n",
|
||||
" \"shuffle\": shuffle,\n",
|
||||
" \"lr_decay\": lr_decay,\n",
|
||||
" \"gamma\": gamma,\n",
|
||||
"}\n",
|
||||
"if steps_per_epoch:\n",
|
||||
" hparams[\"steps_per_epoch\"] = steps_per_epoch\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAINING_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAINING_ACCELARATOR_TYPE,\n",
|
||||
" \"accelerator_count\": TRAINING_ACCELERATOR_COUNT,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAINING_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--task_name=gesture_recognizer\",\n",
|
||||
" \"--training_data_path=%s\" % training_data_path,\n",
|
||||
" \"--model_export_path=%s\" % model_export_path,\n",
|
||||
" \"--evaluation_result_path=%s\" % evaluation_result_path,\n",
|
||||
" \"--split_ratio=%s\" % split_ratio,\n",
|
||||
" \"--model_options=%s\" % json.dumps(model_options),\n",
|
||||
" \"--hparams=%s\" % json.dumps(hparams),\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"training_job = aiplatform.CustomJob(\n",
|
||||
" display_name=TRAINING_JOB_DISPLAY_NAME,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"training_job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "rXMF2tnV_WS0"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluate and export model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mV-Djz-frBni"
|
||||
},
|
||||
"source": [
|
||||
"### Evaluate performance\n",
|
||||
"\n",
|
||||
"After fine-tuning the model, we evaluate the training result on a test dataset, which is typically a portion of your original dataset not used during training. Accuracy levels between 0.8 and 0.9 are generally considered very good, but your use case requirements may differ. You should also consider how fast the model can produce an inference. Higher accuracy frequently comes at the cost of longer inference times.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "09Rz1AYspK19"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_evaluation_result(evaluation_result_path):\n",
|
||||
" try:\n",
|
||||
" with tensorflow.io.gfile.GFile(evaluation_result_path, \"r\") as input_file:\n",
|
||||
" evalutation_result = json.loads(input_file.read())\n",
|
||||
" return evalutation_result[\"accuracy\"], evalutation_result[\"loss\"]\n",
|
||||
" except:\n",
|
||||
" print(\n",
|
||||
" \"Evaluation result not found. Your test dataset is likely \"\n",
|
||||
" + \"empty. You can adjust the size of your test dataset or adjust \"\n",
|
||||
" + \"how you split your dataset.\"\n",
|
||||
" )\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"evaluation_result = get_evaluation_result(EVALUATION_RESULT_OUTPUT_FILE)\n",
|
||||
"\n",
|
||||
"if evaluation_result is not None:\n",
|
||||
" print(\"Accuracy:\", evaluation_result[0])\n",
|
||||
" print(\"Loss:\", evaluation_result[1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"### Export model\n",
|
||||
"After finetuning and evaluating the model, you can save the Tensorflow Lite model, try it out in the [Gesture Recognizer](https://mediapipe-studio.webapps.google.com/demo/gesture_recognizer) demo in MediaPipe Studio or integrate it with your on-device application by following the [Gesture recognizer task guide](https://developers.google.com/mediapipe/solutions/vision/gesture_recognizer). The exported model contains the generates required model metadata, as well as a classification label file."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def copy_model(model_source, model_dest):\n",
|
||||
" ! gsutil cp {model_source} {model_dest}\n",
|
||||
"\n",
|
||||
"copy_model(EXPORTED_MODEL_OUTPUT_FILE, \"gesture_recognizer.task\")\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import files\n",
|
||||
"\n",
|
||||
" files.download(\"gesture_recognizer.task\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete training data and jobs.\n",
|
||||
"if training_job.list(filter=f'display_name=\"{TRAINING_JOB_DISPLAY_NAME}\"'):\n",
|
||||
" training_job.delete()\n",
|
||||
"\n",
|
||||
"!gsutil rm -r {STAGING_BUCKET}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_mediapipe_gesture_recognition.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,619 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden MediaPipe with image classification\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_image_classification.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_image_classification.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_mediapipe_image_classification.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [MediaPipe Model Maker](https://developers.google.com/mediapipe/solutions/model_maker) to train an on-device image classification model in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [custom jobs](https://cloud.google.com/vertex-ai/docs/training/create-custom-job) to train new models\n",
|
||||
" * Export models\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands to install dependencies and to authenticate with Google Cloud if running on Colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install --upgrade pip\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, 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"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "tTy1gX11kCJY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "MzGDU7TWdts_"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-EcIXiGsCePi"
|
||||
},
|
||||
"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": "NIq7R4HZCfIc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l {REGION} -p {PROJECT_ID} {BUCKET_URI}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "960505627ddf"
|
||||
},
|
||||
"source": [
|
||||
"### Import libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "PyQmSRbKA8r-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"import tensorflow\n",
|
||||
"from google.cloud import aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"now = datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
|
||||
"\n",
|
||||
"# The project and bucket are for experiments below.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"# The form for BUCKET_URI is gs://<bucket-name>.\\n\",\n",
|
||||
"BUCKET_URI = \"gs://\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can choose a region from https://cloud.google.com/about/locations.\n",
|
||||
"# Only regions prefixed by \"us\", \"asia\", or \"europe\" are supported.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temp/%s\" % now)\n",
|
||||
"\n",
|
||||
"EVALUATION_RESULT_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"evaluation\")\n",
|
||||
"EVALUATION_RESULT_OUTPUT_FILE = os.path.join(\n",
|
||||
" EVALUATION_RESULT_OUTPUT_DIRECTORY, \"evaluation.json\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"EXPORTED_MODEL_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"model\")\n",
|
||||
"EXPORTED_MODEL_OUTPUT_FILE = os.path.join(\n",
|
||||
" EXPORTED_MODEL_OUTPUT_DIRECTORY, \"model.tflite\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define training machine specs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"TRAINING_JOB_DISPLAY_NAME = \"mediapipe_image_classifier_%s\" % now\n",
|
||||
"TRAINING_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/mediapipe-train\"\n",
|
||||
"TRAINING_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAINING_ACCELARATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"TRAINING_ACCELERATOR_COUNT = 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-rsdAcBV-vlf"
|
||||
},
|
||||
"source": [
|
||||
"## Train your customized models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare input data for training\n",
|
||||
"\n",
|
||||
"Finetuning a model for image classification requires a dataset that includes all kinds of items, or classes, that you want the completed model to be able to identify. You can do this by trimming down a public dataset to only the classes that are relevant to your usecase, compiling your own data, or some combination of both. The dataset can be significantly smaller than what would be required to train a new model from scratch. For example, the [ImageNet](https://www.image-net.org/) dataset used to train many reference models contains millions of images with thousands of categories. Transfer learning with Model Maker can finetune an existing model with a smaller dataset and still perform well, depending on your inference accuracy goals.\n",
|
||||
"\n",
|
||||
"You can re-use an existing dataset such as `gs://cloud-samples-data-us-central1/vision/automl_classification/flowers` to finetune the model or you can upload your own dataset to GCS. If you are using your own dataset, ensure that your image directory contains several subdirectories, each corresponding to specific class labels. Your training data should also follow this pattern: <image_path>/<label_name>/<image_names>.*."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"training_data_path = \"gs://cloud-samples-data-us-central1/vision/automl_classification/flowers\" # @param {type:\"string\"}\n",
|
||||
"split_ratio = \"0.8,0.1,0.1\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"### Set fine-tuning options\n",
|
||||
"\n",
|
||||
"You can pick between different model architectures to further customize your training:\n",
|
||||
"\n",
|
||||
"* MobileNet-V2\n",
|
||||
"* EfficientNet-Lite0\n",
|
||||
"* EfficientNet-Lite2\n",
|
||||
"* EfficientNet-Lite4\n",
|
||||
"\n",
|
||||
"To set the model architecture and other training parameters, adjust the following values:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_architecture = \"mobilenet_v2\" # @param [\"mobilenet_v2\", \"efficientnet_lite0\", \"efficientnet_lite2\", \"efficientnet_lite4\"]\n",
|
||||
"\n",
|
||||
"# The learning rate to use for gradient descent training.\n",
|
||||
"learning_rate: float = 0.01 # @param {type:\"number\"}\n",
|
||||
"# Batch size for training.\n",
|
||||
"batch_size: int = 2 # @param {type:\"number\"}\n",
|
||||
"# Number of training iterations over the dataset.\n",
|
||||
"epochs: int = 10 # @param {type:\"slider\", min:0, max:100, step:1}\n",
|
||||
"# If true, the base module is trained together with the classification layer on\n",
|
||||
"# top.\n",
|
||||
"do_fine_tuning: bool = False # @param {type:\"boolean\"}\n",
|
||||
"# A regularizer that applies a L1 regularization penalty.\n",
|
||||
"l1_regularizer: float = 0.0 # @param {type:\"number\"}\n",
|
||||
"# A regularizer that applies a L2 regularization penalty.\n",
|
||||
"l2_regularizer: float = 0.0001 # @param {type:\"number\"}\n",
|
||||
"# Amount of label smoothing to apply. See tf.keras.losses for more details.\n",
|
||||
"label_smoothing: float = 0.1 # @param {type:\"number\"}\n",
|
||||
"# A boolean controlling whether the training dataset is augmented by randomly\n",
|
||||
"# distorting input images, including random cropping, flipping, etc. See\n",
|
||||
"# utils.image_preprocessing documentation for details.\n",
|
||||
"do_data_augmentation: bool = True # @param {type:\"boolean\"}\n",
|
||||
"# Number of training samples used to calculate the decay steps\n",
|
||||
"# and create the training optimizer.\n",
|
||||
"decay_samples: int = 2560000 # @param {type:\"number\"}\n",
|
||||
"# Number of warmup steps for a linear increasing warmup schedule on learning\n",
|
||||
"# rate. Used to set up warmup schedule by model_util.WarmUp.\n",
|
||||
"warmup_epochs: int = 2 # @param {type:\"number\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"### Run fine-tuning\n",
|
||||
"With your training dataset and fine-tuning options prepared, you are ready to start the fine-tuning process. This process is resource intensive and can take a few minutes to a few hours depending on your available compute resources. On Vertex AI with GPU processing, the example fine-tuning below takes between 4-6 minutes to train on approximately 3700 images.\n",
|
||||
"\n",
|
||||
"To begin the fine-tuning process, use the following code:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_export_path = EXPORTED_MODEL_OUTPUT_DIRECTORY\n",
|
||||
"evaluation_result_path = EVALUATION_RESULT_OUTPUT_DIRECTORY\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAINING_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAINING_ACCELARATOR_TYPE,\n",
|
||||
" \"accelerator_count\": TRAINING_ACCELERATOR_COUNT,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAINING_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--task_name=image_classifier\",\n",
|
||||
" \"--training_data_path=%s\" % training_data_path,\n",
|
||||
" \"--model_export_path=%s\" % model_export_path,\n",
|
||||
" \"--evaluation_result_path=%s\" % evaluation_result_path,\n",
|
||||
" \"--split_ratio=%s\" % split_ratio,\n",
|
||||
" \"--model_architecture=%s\" % model_architecture,\n",
|
||||
" \"--hparams=%s\"\n",
|
||||
" % json.dumps(\n",
|
||||
" {\n",
|
||||
" \"learning_rate\": learning_rate,\n",
|
||||
" \"batch_size\": batch_size,\n",
|
||||
" \"epochs\": epochs,\n",
|
||||
" \"do_fine_tuning\": do_fine_tuning,\n",
|
||||
" \"l1_regularizer\": l1_regularizer,\n",
|
||||
" \"l2_regularizer\": l2_regularizer,\n",
|
||||
" \"label_smoothing\": label_smoothing,\n",
|
||||
" \"do_data_augmentation\": do_data_augmentation,\n",
|
||||
" \"decay_samples\": decay_samples,\n",
|
||||
" \"warmup_epochs\": warmup_epochs,\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"training_job = aiplatform.CustomJob(\n",
|
||||
" display_name=TRAINING_JOB_DISPLAY_NAME,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"training_job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "rXMF2tnV_WS0"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluate and export model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mV-Djz-frBni"
|
||||
},
|
||||
"source": [
|
||||
"### Evaluate performance\n",
|
||||
"\n",
|
||||
"After fine-tuning the model, we evaluate the training result on a test dataset, which is typically a portion of your original dataset not used during training. Accuracy levels between 0.8 and 0.9 are generally considered very good, but your use case requirements may differ. You should also consider how fast the model can produce an inference. Higher accuracy frequently comes at the cost of longer inference times.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "09Rz1AYspK19"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_evaluation_result(evaluation_result_path):\n",
|
||||
" try:\n",
|
||||
" with tensorflow.io.gfile.GFile(evaluation_result_path, \"r\") as input_file:\n",
|
||||
" evalutation_result = json.loads(input_file.read())\n",
|
||||
" return evalutation_result[\"accuracy\"], evalutation_result[\"loss\"]\n",
|
||||
" except:\n",
|
||||
" print(\n",
|
||||
" \"Evaluation result not found. Your test dataset is likely \"\n",
|
||||
" + \"empty. You can adjust the size of your test dataset or adjust \"\n",
|
||||
" + \"how you split your dataset.\"\n",
|
||||
" )\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"evaluation_result = get_evaluation_result(EVALUATION_RESULT_OUTPUT_FILE)\n",
|
||||
"\n",
|
||||
"if evaluation_result is not None:\n",
|
||||
" print(\"Accuracy:\", evaluation_result[0])\n",
|
||||
" print(\"Loss:\", evaluation_result[1])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"### Export model\n",
|
||||
"After finetuning and evaluating the model, you can save the Tensorflow Lite model, try it out in the [Image Classification](https://mediapipe-studio.webapps.google.com/demo/image_classifier) demo in MediaPipe Studio or integrate it with your on-device application by following the [Image classification task guide](https://developers.google.com/mediapipe/solutions/vision/image_classifier). The exported model contains the generates required model metadata, as well as a classification label file."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"def copy_model(model_source, model_dest):\n",
|
||||
" ! gsutil cp {model_source} {model_dest}\n",
|
||||
"\n",
|
||||
"copy_model(EXPORTED_MODEL_OUTPUT_FILE, \"image_classification_model.tflite\")\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import files\n",
|
||||
"\n",
|
||||
" files.download(\"image_classification_model.tflite\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete training data and jobs.\n",
|
||||
"if training_job.list(filter=f'display_name=\"{TRAINING_JOB_DISPLAY_NAME}\"'):\n",
|
||||
" training_job.delete()\n",
|
||||
"\n",
|
||||
"!gsutil rm -r {STAGING_BUCKET}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_mediapipe_image_classification.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden MediaPipe with object detection\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_object_detection.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_object_detection.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_mediapipe_object_detection.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [MediaPipe Model Maker](https://developers.google.com/mediapipe/solutions/model_maker) in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [custom jobs](https://cloud.google.com/vertex-ai/docs/training/create-custom-job) to train new models\n",
|
||||
" * Export models\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands to install dependencies and to authenticate with Google Cloud if running on Colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install --upgrade pip\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, 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"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "tTy1gX11kCJY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "MzGDU7TWdts_"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-EcIXiGsCePi"
|
||||
},
|
||||
"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": "NIq7R4HZCfIc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l {REGION} -p {PROJECT_ID} {BUCKET_URI}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "960505627ddf"
|
||||
},
|
||||
"source": [
|
||||
"### Import libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "PyQmSRbKA8r-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"import tensorflow\n",
|
||||
"from google.cloud import aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"now = datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temp/%s\" % now)\n",
|
||||
"\n",
|
||||
"EVALUATION_RESULT_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"evaluation\")\n",
|
||||
"EVALUATION_RESULT_OUTPUT_FILE = os.path.join(\n",
|
||||
" EVALUATION_RESULT_OUTPUT_DIRECTORY, \"evaluation.json\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"EXPORTED_MODEL_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"model\")\n",
|
||||
"EXPORTED_MODEL_OUTPUT_FILE = os.path.join(\n",
|
||||
" EXPORTED_MODEL_OUTPUT_DIRECTORY, \"model.tflite\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define training machine specs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"TRAINING_JOB_DISPLAY_NAME = \"mediapipe_object_detector_%s\" % now\n",
|
||||
"TRAINING_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/mediapipe-train\"\n",
|
||||
"TRAINING_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAINING_ACCELARATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"TRAINING_ACCELERATOR_COUNT = 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "XDq9TiRUc7dV"
|
||||
},
|
||||
"source": [
|
||||
"## Train your customized models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare input data for training\n",
|
||||
"\n",
|
||||
"Fine-tuning a model for object detection requires a dataset that includes the items, or classes, that you want the completed model to be able to identify. You can do this by trimming down a public dataset to only the classes that are relevant to your usecase, compiling your own dataset, or some combination of both, The dataset can be significantly smaller than what would be required to train a new model from scratch. For example, the [COCO](https://cocodataset.org/) dataset used to train many reference models contains hundreds of thousands of images with 91 classes of objects. Transfer learning with Model Maker can finetune an existing model with a smaller dataset and still perform well, depending on your inference accuracy goals. These instructions use a smaller dataset containing 2 types of android figurines, or 2 classes, with 62 total training images.\n",
|
||||
"\n",
|
||||
"You can re-use an existing dataset such as `gs://mediapipe-tasks/object_detector/android_figurine` to finetune the model. The directory contains two subdirectories for the training and validation datasets, located in android_figurine/train and android_figurine/validation respectively. Each of the train and validation datasets follow the COCO Dataset format described below. If you are using your own dataset, ensure that that it adheres to the format specifications before uploading it to Google Cloud Storage.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"### Supported dataset formats\n",
|
||||
"Model Maker Object Detection API supports reading the following dataset formats:\n",
|
||||
"\n",
|
||||
"#### COCO format\n",
|
||||
"The COCO dataset format has a `data` directory which stores all of the images and a single `labels.json` file which contains the object annotations for all images.\n",
|
||||
"```\n",
|
||||
"<dataset_dir>/\n",
|
||||
" data/\n",
|
||||
" <img0>.<jpg/jpeg>\n",
|
||||
" <img1>.<jpg/jpeg>\n",
|
||||
" ...\n",
|
||||
" labels.json\n",
|
||||
"```\n",
|
||||
"where `labels.json` is formatted as:\n",
|
||||
"```\n",
|
||||
"{\n",
|
||||
" \"categories\":[\n",
|
||||
" {\"id\":1, \"name\":<cat1_name>},\n",
|
||||
" ...\n",
|
||||
" ],\n",
|
||||
" \"images\":[\n",
|
||||
" {\"id\":0, \"file_name\":\"<img0>.<jpg/jpeg>\"},\n",
|
||||
" ...\n",
|
||||
" ],\n",
|
||||
" \"annotations\":[\n",
|
||||
" {\"id\":0, \"image_id\":0, \"category_id\":1, \"bbox\":[x-top left, y-top left, width, height]},\n",
|
||||
" ...\n",
|
||||
" ]\n",
|
||||
"}\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"#### PASCAL VOC format\n",
|
||||
"\n",
|
||||
"The PASCAL VOC dataset format also has a `data` directory which stores all of the images, however the annotations are split up per image into corresponding xml files in the `Annotations` directory.\n",
|
||||
"```\n",
|
||||
"<dataset_dir>/\n",
|
||||
" data/\n",
|
||||
" <file0>.<jpg/jpeg>\n",
|
||||
" ...\n",
|
||||
" Annotations/\n",
|
||||
" <file0>.xml\n",
|
||||
" ...\n",
|
||||
"```\n",
|
||||
"where the xml files are formatted as:\n",
|
||||
"```\n",
|
||||
"<annotation>\n",
|
||||
" <filename>file0.jpg</filename>\n",
|
||||
" <object>\n",
|
||||
" <name>kangaroo</name>\n",
|
||||
" <bndbox>\n",
|
||||
" <xmin>233</xmin>\n",
|
||||
" <ymin>89</ymin>\n",
|
||||
" <xmax>386</xmax>\n",
|
||||
" <ymax>262</ymax>\n",
|
||||
" </bndbox>\n",
|
||||
" </object>\n",
|
||||
" <object>\n",
|
||||
" ...\n",
|
||||
" </object>\n",
|
||||
" ...\n",
|
||||
"</annotation>\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "O32DU5RRGhdV"
|
||||
},
|
||||
"source": [
|
||||
"### Configure training dataset\n",
|
||||
"\n",
|
||||
"Once you have completed preparing your data, you can begin fine-tuning a model to recognize the new objects, or classes, defined by your training data. The instructions below use the data prepared in the previous section to finetune an object detection model to recognize the two types of android figurines.\n",
|
||||
"\n",
|
||||
"You can leave the path to the test data empty if you do not have a separate test data set."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"training_data_path = \"gs://mediapipe-tasks/object_detector/android_figurine/train\" # @param {type:\"string\"}\n",
|
||||
"validation_data_path = \"gs://mediapipe-tasks/object_detector/android_figurine/validation\" # @param {type:\"string\"}\n",
|
||||
"test_data_path = \"\" # @param {type:\"string\"}\n",
|
||||
"data_format = \"coco\" # @param [\"coco\", \"pascal_voc\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"### Set fine-tuning options\n",
|
||||
"\n",
|
||||
"You can pick between different model architectures to further customize your training:\n",
|
||||
"\n",
|
||||
"* MobileNet-V2\n",
|
||||
"* MobileNet-MultiHW-AVG\n",
|
||||
"\n",
|
||||
"To set the model architecture and other training parameters, adjust the following values:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_architecture = \"mobilenet_v2\" # @param [\"mobilenet_v2\", \"mobilenet_multihw_avg\"]\n",
|
||||
"\n",
|
||||
"# The learning rate to use for gradient descent training.\n",
|
||||
"learning_rate: float = 0.01 # @param {type:\"number\"}\n",
|
||||
"# Batch size for training.\n",
|
||||
"batch_size: int = 2 # @param {type:\"number\"}\n",
|
||||
"# Number of training iterations over the dataset.\n",
|
||||
"epochs: int = 10 # @param {type:\"slider\", min:0, max:100, step:1}\n",
|
||||
"# If true, the base module is trained together with the classification layer on\n",
|
||||
"# top.\n",
|
||||
"do_fine_tuning: bool = False # @param {type:\"boolean\"}\n",
|
||||
"# A regularizer that applies a L1 regularization penalty.\n",
|
||||
"l1_regularizer: float = 0.0 # @param {type:\"number\"}\n",
|
||||
"# A regularizer that applies a L2 regularization penalty.\n",
|
||||
"l2_regularizer: float = 0.0001 # @param {type:\"number\"}\n",
|
||||
"# A boolean controlling whether the training dataset is augmented by randomly\n",
|
||||
"# distorting input images, including random cropping, flipping, etc. See\n",
|
||||
"# utils.image_preprocessing documentation for details.\n",
|
||||
"do_data_augmentation: bool = True # @param {type:\"boolean\"}\n",
|
||||
"# Number of training samples used to calculate the decay steps\n",
|
||||
"# and create the training optimizer.\n",
|
||||
"decay_samples: int = 2560000 # @param {type:\"number\"}\n",
|
||||
"# Number of warmup steps for a linear increasing warmup schedule on learning\n",
|
||||
"# rate. Used to set up warmup schedule by model_util.WarmUp.\n",
|
||||
"warmup_epochs: int = 2 # @param {type:\"number\"}\n",
|
||||
"# The number of epochs for cosine decay learning rate.\n",
|
||||
"cosine_decay_epochs: int = 5 # @param {type:\"number\"}\n",
|
||||
"# The alpha value for cosine decay learning rate.\n",
|
||||
"cosine_decay_alpha: float = 5 # @param {type:\"number\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"### Run fine-tuning\n",
|
||||
"With your training dataset and fine-tuning options prepared, you are ready to start the fine-tuning process. This process is resource intensive and can take a few minutes to a few hours depending on your available compute resources. This process is resource intensive and can take a few minutes to a few hours depending on your available compute resources. On Vertex AI with GPU processing, the example fine-tuning below takes about 3 to 4 minutes.\n",
|
||||
"\n",
|
||||
"To begin the fine-tuning process, use the following code:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_export_path = EXPORTED_MODEL_OUTPUT_DIRECTORY\n",
|
||||
"evaluation_result_path = EVALUATION_RESULT_OUTPUT_DIRECTORY\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAINING_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAINING_ACCELARATOR_TYPE,\n",
|
||||
" \"accelerator_count\": TRAINING_ACCELERATOR_COUNT,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAINING_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--task_name=object_detector\",\n",
|
||||
" \"--training_data_path=%s\" % training_data_path,\n",
|
||||
" \"--validation_data_path=%s\" % validation_data_path,\n",
|
||||
" \"--test_data_path=%s\" % test_data_path,\n",
|
||||
" \"--data_format=%s\" % data_format,\n",
|
||||
" \"--model_export_path=%s\" % model_export_path,\n",
|
||||
" \"--evaluation_result_path=%s\" % evaluation_result_path,\n",
|
||||
" \"--model_architecture=%s\" % model_architecture,\n",
|
||||
" \"--hparams=%s\"\n",
|
||||
" % json.dumps(\n",
|
||||
" {\n",
|
||||
" \"learning_rate\": learning_rate,\n",
|
||||
" \"batch_size\": batch_size,\n",
|
||||
" \"epochs\": epochs,\n",
|
||||
" \"do_fine_tuning\": do_fine_tuning,\n",
|
||||
" \"l1_regularizer\": l1_regularizer,\n",
|
||||
" \"l2_regularizer\": l2_regularizer,\n",
|
||||
" \"do_data_augmentation\": do_data_augmentation,\n",
|
||||
" \"decay_samples\": decay_samples,\n",
|
||||
" \"warmup_epochs\": warmup_epochs,\n",
|
||||
" \"cosine_decay_epochs\": cosine_decay_epochs,\n",
|
||||
" \"cosine_decay_alpha\": cosine_decay_alpha,\n",
|
||||
" }\n",
|
||||
" ),\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"training_job = aiplatform.CustomJob(\n",
|
||||
" display_name=TRAINING_JOB_DISPLAY_NAME,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"training_job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zcKzIa5QeIIU"
|
||||
},
|
||||
"source": [
|
||||
"## Evaluate and export model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mV-Djz-frBni"
|
||||
},
|
||||
"source": [
|
||||
"### Evaluate performance\n",
|
||||
"\n",
|
||||
"If you have specified test data, you can evaluate it on the test dataset and print the loss and coco metrics. The most important metric for evaluating the model performance is typically the \"AP\" coco metric for Average Precision.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "09Rz1AYspK19"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def get_evaluation_result(evaluation_result_path):\n",
|
||||
" try:\n",
|
||||
" with tensorflow.io.gfile.GFile(evaluation_result_path, \"r\") as input_file:\n",
|
||||
" evalutation_result = json.loads(input_file.read())\n",
|
||||
" return evalutation_result[\"loss\"], evalutation_result[\"coco_metrics\"]\n",
|
||||
" except:\n",
|
||||
" print(\"Evaluation result not found. Did you provide a test dataset?\")\n",
|
||||
" return None\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"evaluation_result = get_evaluation_result(EVALUATION_RESULT_OUTPUT_FILE)\n",
|
||||
"\n",
|
||||
"if evaluation_result is not None:\n",
|
||||
" print(f\"Validation loss: {evaluation_result[0]}\")\n",
|
||||
" print(f\"Validation coco metrics: {evaluation_result[1]}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"### Export model\n",
|
||||
"After fine-tuning and evaluating the model, you can save it as Tensorflow Lite model, try it out in the [Object Detector](https://mediapipe-studio.webapps.google.com/demo/object_detector) demo in MediaPipe Studio or integrate it with your application by following the [Object detection task guide](https://developers.google.com/mediapipe/solutions/vision/object_detector). The exported model also includes metadata and the label map."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def copy_model(model_source, model_dest):\n",
|
||||
" ! gsutil cp {model_source} {model_dest}\n",
|
||||
"\n",
|
||||
"copy_model(EXPORTED_MODEL_OUTPUT_FILE, \"object_detection_model.tflite\")\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import files\n",
|
||||
"\n",
|
||||
" files.download(\"object_detection_model.tflite\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete training data and jobs.\n",
|
||||
"if training_job.list(filter=f'display_name=\"{TRAINING_JOB_DISPLAY_NAME}\"'):\n",
|
||||
" training_job.delete()\n",
|
||||
"\n",
|
||||
"!gsutil rm -r {STAGING_BUCKET}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_mediapipe_object_detection.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden MediaPipe with text classification\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_text_classification.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_text_classification.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_mediapipe_image_classification.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [MediaPipe Model Maker](https://developers.google.com/mediapipe/solutions/model_maker) to train an on-device text classification model in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [custom jobs](https://cloud.google.com/vertex-ai/docs/training/create-custom-job) to train new models\n",
|
||||
" * Export models\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands to install dependencies and to authenticate with Google Cloud if running on Colab."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install --upgrade pip\n",
|
||||
"\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, 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"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "tTy1gX11kCJY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "MzGDU7TWdts_"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-EcIXiGsCePi"
|
||||
},
|
||||
"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": "NIq7R4HZCfIc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l {REGION} -p {PROJECT_ID} {BUCKET_URI}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "960505627ddf"
|
||||
},
|
||||
"source": [
|
||||
"### Import libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "PyQmSRbKA8r-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"now = datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temp/%s\" % now)\n",
|
||||
"\n",
|
||||
"EVALUATION_RESULT_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"evaluation\")\n",
|
||||
"EVALUATION_RESULT_OUTPUT_FILE = os.path.join(\n",
|
||||
" EVALUATION_RESULT_OUTPUT_DIRECTORY, \"evaluation.json\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"EXPORTED_MODEL_OUTPUT_DIRECTORY = os.path.join(STAGING_BUCKET, \"model\")\n",
|
||||
"EXPORTED_MODEL_OUTPUT_FILE = os.path.join(\n",
|
||||
" EXPORTED_MODEL_OUTPUT_DIRECTORY, \"model.tflite\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define training machine specs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"TRAINING_JOB_DISPLAY_NAME = \"mediapipe_text_classifier_%s\" % now\n",
|
||||
"TRAINING_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/mediapipe-train\"\n",
|
||||
"TRAINING_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAINING_ACCELARATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"TRAINING_ACCELERATOR_COUNT = 2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-rsdAcBV-vlf"
|
||||
},
|
||||
"source": [
|
||||
"## Train your customized models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Get the Dataset\n",
|
||||
"\n",
|
||||
"The following code block uses the [SST-2](https://nlp.stanford.edu/sentiment/index.html) (Stanford Sentiment Treebank) dataset which contains 67,349 movie reviews for training and 872 movie reviews for testing. The dataset has two classes: positive and negative movie reviews. Positive reviews are labeled with 1 and negative reviews with 0.\n",
|
||||
"\n",
|
||||
"The SST-2 dataset is stored as a TSV file. The only difference between the TSV and CSV formats is that TSV uses a tab `\\t` character as its delimiter and CSV uses a comma `,`.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"training_data_path = (\n",
|
||||
" \"gs://mediapipe-tasks/text_classifier/SST-2/train.tsv\" # @param {type:\"string\"}\n",
|
||||
")\n",
|
||||
"validation_data_path = (\n",
|
||||
" \"gs://mediapipe-tasks/text_classifier/SST-2/dev.tsv\" # @param {type:\"string\"}\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# The delimiter used in the dataset.\n",
|
||||
"delimiter = \"\\t\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Character used to quote fields that contain special characters\n",
|
||||
"# like the `delimiter`.\n",
|
||||
"quotechar = \"\\t\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Sequence of keys for the CSV columns (represented as a comma\n",
|
||||
"# separated list). If empty, the first row of the CSV file is used\n",
|
||||
"# as the keys\n",
|
||||
"fieldnames = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Column name for the input text.\n",
|
||||
"text_column = \"sentence\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Column name for the labels.\n",
|
||||
"label_column = \"label\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"### Set fine-tuning options\n",
|
||||
"\n",
|
||||
"You can pick between different model architectures to further customize your training:\n",
|
||||
"\n",
|
||||
"* Average Word Embedding Model\n",
|
||||
"* BERT-classifier\n",
|
||||
"\n",
|
||||
"To set the model architecture and other training parameters, adjust the following values:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_architecture = (\n",
|
||||
" \"average_word_embedding\" # @param [\"average_word_embedding\", \"mobilebert\"]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# The learning rate to use for gradient descent-based\n",
|
||||
"# optimizers. Defaults to 3e-5 for the BERT-based classifier\n",
|
||||
"# and 0 for the average word-embedding classifier because\n",
|
||||
"# it does not need such an optimizer.\n",
|
||||
"learning_rate: float = 0.0 # @param {type:\"number\"}\n",
|
||||
"\n",
|
||||
"# Batch size for training. Defaults to 32 for the average\n",
|
||||
"# word-embedding classifier and 48 for the BERT-based\n",
|
||||
"# classifier.\n",
|
||||
"batch_size: int = 48 # @param {type:\"number\"}\n",
|
||||
"\n",
|
||||
"# Number of training iterations over the dataset. Defaults\n",
|
||||
"# to 10 for the average word-embedding classifier and 3\n",
|
||||
"# for the BERT-based classifier.\n",
|
||||
"epochs: int = 10 # @param {type:\"slider\", min:0, max:100, step:1}\n",
|
||||
"\n",
|
||||
"# An integer that indicates the number of training steps per\n",
|
||||
"# epoch. If set to 0, the training pipeline calculates the\n",
|
||||
"# default steps per epoch as the training dataset size\n",
|
||||
"# divided by batch size.\n",
|
||||
"steps_per_epoch: int = 0 # @param {type:\"number\"}\n",
|
||||
"\n",
|
||||
"# Controls whether the dataset is shuffled before training.\n",
|
||||
"shuffle: bool = False # @param {type:\"boolean\"}\n",
|
||||
"\n",
|
||||
"# Length of the sequence to feed into the model.\n",
|
||||
"seq_len: int = 256 # @param {type:\"number\"}\n",
|
||||
"\n",
|
||||
"# Whether to convert all uppercase characters to lowercase\n",
|
||||
"# during preprocessing.\n",
|
||||
"do_lower_case: bool = True # @param {type:\"boolean\"}\n",
|
||||
"\n",
|
||||
"# The rate for dropout.\n",
|
||||
"dropout_rate: float = 0.2 # @param {type:\"number\"}\n",
|
||||
"\n",
|
||||
"# Dimension of the word embedding. Only used for the Average Word\n",
|
||||
"# Embedding Model.\n",
|
||||
"wordvec_dim: int = 16 # @param {type:\"number\"}\n",
|
||||
"\n",
|
||||
"# Number of words to generate the vocabulary from data.\n",
|
||||
"# Only used for the Average Word Embedding Model.\n",
|
||||
"vocab_size: int = 10000 # @param {type:\"number\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"### Run fine-tuning\n",
|
||||
"With your training dataset and fine-tuning options prepared, you are ready to start the fine-tuning process. This process is resource intensive and can take a few minutes to a few hours depending on the model archtiecture and your available compute resources. On Vertex AI with GPU processing, the example fine-tuning below takes between 2-3 minutes to train an Average Word Embedding Model on the SST-2 dataset.\n",
|
||||
"\n",
|
||||
"To begin the fine-tuning process, use the following code:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_export_path = EXPORTED_MODEL_OUTPUT_DIRECTORY\n",
|
||||
"evaluation_result_path = EVALUATION_RESULT_OUTPUT_DIRECTORY\n",
|
||||
"\n",
|
||||
"preprocessing_params = {\n",
|
||||
" \"text_column\": text_column,\n",
|
||||
" \"label_column\": label_column,\n",
|
||||
" \"delimiter\": delimiter,\n",
|
||||
" \"quotechar\": quotechar,\n",
|
||||
"}\n",
|
||||
"if fieldnames:\n",
|
||||
" preprocessing_params[\"fieldnames\"] = [\n",
|
||||
" fieldname.strip() for fieldname in fieldnames.split(\",\")\n",
|
||||
" ]\n",
|
||||
"\n",
|
||||
"hparams = {\n",
|
||||
" \"learning_rate\": learning_rate,\n",
|
||||
" \"batch_size\": batch_size,\n",
|
||||
" \"epochs\": epochs,\n",
|
||||
" \"shuffle\": shuffle,\n",
|
||||
"}\n",
|
||||
"if steps_per_epoch:\n",
|
||||
" hparams[\"steps_per_epoch\"] = steps_per_epoch\n",
|
||||
"\n",
|
||||
"model_options = {\n",
|
||||
" \"dropout_rate\": dropout_rate,\n",
|
||||
" \"wordvec_dim\": wordvec_dim,\n",
|
||||
" \"do_lower_case\": do_lower_case,\n",
|
||||
" \"vocab_size\": vocab_size,\n",
|
||||
" \"dropout_rate\": dropout_rate,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAINING_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAINING_ACCELARATOR_TYPE,\n",
|
||||
" \"accelerator_count\": TRAINING_ACCELERATOR_COUNT,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAINING_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--task_name=text_classifier\",\n",
|
||||
" \"--training_data_path=%s\" % training_data_path,\n",
|
||||
" \"--validation_data_path=%s\" % validation_data_path,\n",
|
||||
" \"--evaluation_result_path=%s\" % evaluation_result_path,\n",
|
||||
" \"--model_export_path=%s\" % model_export_path,\n",
|
||||
" \"--model_architecture=%s\" % model_architecture,\n",
|
||||
" \"--preprocessing_params=%s\" % json.dumps(preprocessing_params),\n",
|
||||
" \"--hparams=%s\" % json.dumps(hparams),\n",
|
||||
" \"--model_options=%s\" % json.dumps(model_options),\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"training_job = aiplatform.CustomJob(\n",
|
||||
" display_name=TRAINING_JOB_DISPLAY_NAME,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"training_job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "rXMF2tnV_WS0"
|
||||
},
|
||||
"source": [
|
||||
"## Export model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"After finetuning, you can save the Tensorflow Lite model, try it out in the [Text Classification](https://mediapipe-studio.webapps.google.com/demo/text_classifier) demo in MediaPipe Studio or integrate it with your on-device application by following the [Text classification task guide](https://developers.google.com/mediapipe/solutions/text/text_classifier). The exported model contains the generates required model metadata, as well as a classification label file."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def copy_model(model_source, model_dest):\n",
|
||||
" ! gsutil cp {model_source} {model_dest}\n",
|
||||
"\n",
|
||||
"copy_model(EXPORTED_MODEL_OUTPUT_FILE, \"text_classification_model.tflite\")\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" from google.colab import files\n",
|
||||
"\n",
|
||||
" files.download(\"text_classification_model.tflite\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete training data and jobs.\n",
|
||||
"if training_job.list(filter=f'display_name=\"{TRAINING_JOB_DISPLAY_NAME}\"'):\n",
|
||||
" training_job.delete()\n",
|
||||
"\n",
|
||||
"!gsutil rm -r {STAGING_BUCKET}"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_mediapipe_text_classification.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,873 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden MoViNet video clip classification\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_movinet_clip_classification.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_movinet_clip_classification.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> <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_movinet_clip_classification.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [MoViNet](https://github.com/tensorflow/models/tree/master/official/projects/movinet) in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [hyperparameter tuning jobs](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) to train new models\n",
|
||||
" * Find and export best models\n",
|
||||
"\n",
|
||||
"* Test trained models\n",
|
||||
" * Upload models to the [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction)\n",
|
||||
" * Run batch predictions\n",
|
||||
"\n",
|
||||
"* Clean up resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab Only\n",
|
||||
"Run the following commands for Colab or skip this section if you use Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"### 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 API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\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": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# The GCP project ID for experiments.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Bucket URI with gs:// prefix.\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can choose a region from https://cloud.google.com/about/locations.\n",
|
||||
"# Only regions prefixed by \"us\", \"asia\", or \"europe\" are supported.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"CHECKPOINT_BUCKET = os.path.join(BUCKET_URI, \"ckpt\")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
|
||||
"\n",
|
||||
"# Download config files.\n",
|
||||
"CONFIG_DIR = os.path.join(BUCKET_URI, \"config\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"OBJECTIVE = \"vcn\"\n",
|
||||
"\n",
|
||||
"# Data converter constants.\n",
|
||||
"DATA_CONVERTER_JOB_PREFIX = \"data_converter\"\n",
|
||||
"DATA_CONVERTER_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/data-converter\"\n",
|
||||
"DATA_CONVERTER_MACHINE_TYPE = \"n1-highmem-8\"\n",
|
||||
"\n",
|
||||
"# Training constants.\n",
|
||||
"TRAINING_JOB_PREFIX = \"train\"\n",
|
||||
"TRAIN_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/movinet-train\"\n",
|
||||
"TRAIN_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAIN_ACCELERATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"TRAIN_NUM_GPU = 2\n",
|
||||
"\n",
|
||||
"# Evaluation constants.\n",
|
||||
"EVALUATION_METRIC = \"accuracy\"\n",
|
||||
"\n",
|
||||
"# Export constants.\n",
|
||||
"EXPORT_JOB_PREFIX = \"export\"\n",
|
||||
"EXPORT_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/movinet-model-export\"\n",
|
||||
"EXPORT_MACHINE_TYPE = \"n1-highmem-8\"\n",
|
||||
"\n",
|
||||
"# Prediction constants.\n",
|
||||
"# You can adjust accelerator types and machine types to get faster predictions.\n",
|
||||
"UPLOAD_JOB_PREFIX = \"upload\"\n",
|
||||
"PREDICTION_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/movinet-serve\"\n",
|
||||
"PREDICTION_PORT = 8501\n",
|
||||
"PREDICTION_ACCELERATOR_COUNT = 1\n",
|
||||
"PREDICTION_ACCELERATOR_TYPE = \"NVIDIA_TESLA_T4\"\n",
|
||||
"PREDICTION_MACHINE_TYPE = \"n1-standard-4\"\n",
|
||||
"PREDICTION_JOB_PREFIX = \"predict\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZZFPe_GezXg8"
|
||||
},
|
||||
"source": [
|
||||
"### Define common helper functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"import tensorflow as tf\n",
|
||||
"import yaml\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" \"\"\"Returns a timestamped job name with the given prefix.\"\"\"\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_response_instance(json_str: str, label_map: dict[int, str]):\n",
|
||||
" \"\"\"Prints summary of a prediction JSON result from the model response.\"\"\"\n",
|
||||
" json_obj = json.loads(json_str)\n",
|
||||
" if \"prediction\" not in json_obj:\n",
|
||||
" print(\"Error:\", json_str)\n",
|
||||
" return\n",
|
||||
" instance = json_obj[\"instance\"]\n",
|
||||
" prediction = json_obj[\"prediction\"]\n",
|
||||
" gcs_uri = instance[\"content\"]\n",
|
||||
" time_start = instance.get(\"timeSegmentStart\", \"0.0s\")\n",
|
||||
" time_end = instance.get(\"timeSegmentEnd\", \"Infinity\")\n",
|
||||
" max_idx = np.argmax(prediction)\n",
|
||||
" print(f\"{gcs_uri} {time_start}-{time_end}:\", label_map[max_idx])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_label_map(label_map_yaml_filepath: str) -> tuple[dict[int, str], int]:\n",
|
||||
" \"\"\"Reads label map from a YAML file and returns the label map with the number of classes.\"\"\"\n",
|
||||
" with tf.io.gfile.GFile(label_map_yaml_filepath, \"rb\") as input_file:\n",
|
||||
" label_map = yaml.safe_load(input_file.read())[\"label_map\"]\n",
|
||||
" num_classes = max(label_map.keys()) + 1\n",
|
||||
" return label_map, num_classes\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_best_trial(model_dir, max_trial_count, evaluation_metric):\n",
|
||||
" \"\"\"Finds the best trial directory and eval results from a hyperparameter tuning job.\"\"\"\n",
|
||||
" best_trial_dir = \"\"\n",
|
||||
" best_trial_evaluation_results = {}\n",
|
||||
" best_performance = -1\n",
|
||||
"\n",
|
||||
" for i in range(max_trial_count):\n",
|
||||
" current_trial = i + 1\n",
|
||||
" current_trial_dir = os.path.join(model_dir, \"trial_\" + str(current_trial))\n",
|
||||
" current_trial_best_ckpt_dir = os.path.join(current_trial_dir, \"best_ckpt\")\n",
|
||||
" current_trial_best_ckpt_evaluation_filepath = os.path.join(\n",
|
||||
" current_trial_best_ckpt_dir, \"info.json\"\n",
|
||||
" )\n",
|
||||
" with tf.io.gfile.GFile(current_trial_best_ckpt_evaluation_filepath, \"rb\") as f:\n",
|
||||
" eval_metric_results = json.load(f)\n",
|
||||
" current_performance = eval_metric_results[evaluation_metric]\n",
|
||||
" if current_performance > best_performance:\n",
|
||||
" best_performance = current_performance\n",
|
||||
" best_trial_dir = current_trial_dir\n",
|
||||
" best_trial_evaluation_results = eval_metric_results\n",
|
||||
" return best_trial_dir, best_trial_evaluation_results\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def find_checkpoint_in_dir(checkpoint_dir: str):\n",
|
||||
" \"\"\"Finds a checkpoint path relative to the directory.\"\"\"\n",
|
||||
" for root, dirs, files in tf.io.gfile.walk(checkpoint_dir):\n",
|
||||
" for file in files:\n",
|
||||
" if file.endswith(\".index\"):\n",
|
||||
" return os.path.join(root, os.path.splitext(file)[0])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_checkpoint_to_gcs(checkpoint_url: str):\n",
|
||||
" \"\"\"Uploads a compressed .tar.gz checkpoint at the given URL to Cloud Storage.\"\"\"\n",
|
||||
" filename = os.path.basename(checkpoint_url)\n",
|
||||
" checkpoint_name = filename.replace(\".tar.gz\", \"\")\n",
|
||||
" print(\"Download checkpoint from\", checkpoint_url, \"and store to\", CHECKPOINT_BUCKET)\n",
|
||||
" ! wget $checkpoint_url -O $filename\n",
|
||||
" ! mkdir -p $checkpoint_name\n",
|
||||
" ! tar -xvzf $filename -C $checkpoint_name\n",
|
||||
"\n",
|
||||
" checkpoint_path = find_checkpoint_in_dir(checkpoint_name)\n",
|
||||
" checkpoint_path = os.path.relpath(checkpoint_path, checkpoint_name)\n",
|
||||
"\n",
|
||||
" ! gsutil cp -r $checkpoint_name $CHECKPOINT_BUCKET/\n",
|
||||
" checkpoint_uri = os.path.join(CHECKPOINT_BUCKET, checkpoint_name, checkpoint_path)\n",
|
||||
" print(\"Checkpoint uploaded to\", checkpoint_uri)\n",
|
||||
" return checkpoint_uri\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_config_to_gcs(url: str):\n",
|
||||
" \"\"\"Uploads a config file at the given URL to Cloud Storage.\"\"\"\n",
|
||||
" filename = os.path.basename(url)\n",
|
||||
" destination = os.path.join(CONFIG_DIR, filename)\n",
|
||||
" print(\"Copy\", url, \"to\", destination)\n",
|
||||
" ! wget \"$url\" -O \"$filename\"\n",
|
||||
" ! gsutil cp \"$filename\" \"$destination\"\n",
|
||||
" return destination"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RB_xY9ipr7ZU"
|
||||
},
|
||||
"source": [
|
||||
"## Train new models\n",
|
||||
"This section shows how to train new models.\n",
|
||||
"1. Convert input data to training formats\n",
|
||||
"2. Create hyperparameter tuning jobs to train new models\n",
|
||||
"3. Find and export best models\n",
|
||||
"\n",
|
||||
"If you already trained models, please go to the section `Test Trained models`.\n",
|
||||
"\n",
|
||||
"Please select a model:\n",
|
||||
"* `model_id`: MoViNet model variant ID, one of `a0`, `a1`, `a2`, `a3`, `a4`, `a5`. The model with a larger number requires more resources to train, and is expected to have a higher accuracy and latency. Here, we use `a0` for demonstration purpose.\n",
|
||||
"* `model_mode`: MoViNet model type, either `base` or `stream`. The base model has a slightly higher accuracy, while the streaming model is optimized for streaming and faster CPU inference. See [official MoViNet docs](https://github.com/tensorflow/models/tree/master/official/projects/movinet) for more information.\n",
|
||||
"\n",
|
||||
"**Note**: The prediction container only supports base model (non-streaming) for now. If you train a streaming model, you need to download the model and refer to the [MoViNet official guide](https://github.com/tensorflow/models/blob/master/official/projects/movinet/movinet_streaming_model_training_and_inference.ipynb) for running predictions locally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "3Ry1mw6AHLTy"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_id = \"a0\" # @param [\"a0\", \"a1\", \"a2\", \"a3\", \"a4\", \"a5\"]\n",
|
||||
"model_mode = \"base\" # @param [\"base\", \"stream\"]\n",
|
||||
"is_stream = model_mode == \"stream\"\n",
|
||||
"model_name = f\"movinet_{model_id}_{model_mode}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare input data for training\n",
|
||||
"\n",
|
||||
"Prepare data in the format as described [here](https://cloud.google.com/vertex-ai/docs/video-data/classification/prepare-data), and then convert them to the training formats by running the cell below:\n",
|
||||
"\n",
|
||||
"* `input_file_path`: The input file path to the prepared data.\n",
|
||||
"* `input_file_type`: The input file type, such as `csv` or `jsonl`.\n",
|
||||
"* `split_ratio`: Three comma separated floats indicating the proportion of data to split into train/validation/test. They must add up to 1.\n",
|
||||
"* `num_shard`: Three comma separated integers indicating the shards for train/validation/test.\n",
|
||||
"* `output_dir`: The output directory, which will contain converted train/test/validation data.\n",
|
||||
"* `output_fps`: The sampling rate of the video; Frames per second."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This job will convert input data as training format, with given split ratios\n",
|
||||
"# and number of shards on train/test/validation.\n",
|
||||
"\n",
|
||||
"data_converter_job_name = get_job_name_with_datetime(\n",
|
||||
" DATA_CONVERTER_JOB_PREFIX + \"_\" + OBJECTIVE\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"input_file_path = \"\" # @param {type:\"string\"}\n",
|
||||
"input_file_type = \"csv\" # @param [\"csv\", \"jsonl\"]\n",
|
||||
"output_fps = 5 # @param {type:\"integer\"}\n",
|
||||
"split_ratio = \"0.8,0.1,0.1\"\n",
|
||||
"num_shard = \"10,10,10\"\n",
|
||||
"data_converter_output_dir = os.path.join(BUCKET_URI, data_converter_job_name)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": DATA_CONVERTER_MACHINE_TYPE,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": DATA_CONVERTER_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--input_file_path=%s\" % input_file_path,\n",
|
||||
" \"--input_file_type=%s\" % input_file_type,\n",
|
||||
" \"--objective=%s\" % OBJECTIVE,\n",
|
||||
" \"--num_shard=%s\" % num_shard,\n",
|
||||
" \"--split_ratio=%s\" % split_ratio,\n",
|
||||
" \"--output_dir=%s\" % data_converter_output_dir,\n",
|
||||
" \"--output_fps=%d\" % output_fps,\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"data_converter_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=data_converter_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"data_converter_custom_job.run()\n",
|
||||
"\n",
|
||||
"input_train_data_path = os.path.join(data_converter_output_dir, \"train.tfrecord*\")\n",
|
||||
"input_validation_data_path = os.path.join(data_converter_output_dir, \"val.tfrecord*\")\n",
|
||||
"label_map_path = os.path.join(data_converter_output_dir, \"label_map.yaml\")\n",
|
||||
"print(\"input_train_data_path for training: \", input_train_data_path)\n",
|
||||
"print(\"input_validation_data_path for training: \", input_validation_data_path)\n",
|
||||
"print(\"label_map_path for prediction: \", label_map_path)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Vertex AI custom job with hyperparameter tuning\n",
|
||||
"\n",
|
||||
"You use the Vertex AI SDK to create and run the [hyperparameter tuning job](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) with Vertex AI Model Garden training docker images.\n",
|
||||
"\n",
|
||||
"#### Define the following specifications\n",
|
||||
"\n",
|
||||
"* `worker_pool_specs`: A list of dictionaries specifying the machine type and docker image. This example defines a single node cluster with one `n1-standard-4` machine with 2 `NVIDIA_TESLA_V100` GPUs.\n",
|
||||
"\n",
|
||||
" **Note**: We recommend using 8 GPUs for MoViNet-A2 and larger. Since loading video data requires a lot of GPU memory, it is recommended to experiment with a small batch size first.\n",
|
||||
"* `parameter_spec`: Dictionary specifying the parameters to optimize. The dictionary key is the string assigned to the command line argument for each hyperparameter in your training application code, and the dictionary value is the parameter specification. The parameter specification includes the type, min/max values, and scale for the hyperparameter.\n",
|
||||
"* `metric_spec`: Dictionary specifying the metric to optimize. The dictionary key is the `hyperparameter_metric_tag` that you set in your training application code, and the value is the optimization goal."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
|
||||
"\n",
|
||||
"# Input train and validation datasets can be found from the section above\n",
|
||||
"# `Prepare input data for training`.\n",
|
||||
"# Or, set prepared datasets paths if already exist.\n",
|
||||
"# input_train_data_path = \"\"\n",
|
||||
"# input_validation_data_path = \"\"\n",
|
||||
"# label_map_path = \"\"\n",
|
||||
"\n",
|
||||
"train_job_name = get_job_name_with_datetime(f\"{TRAINING_JOB_PREFIX}_{model_name}\")\n",
|
||||
"model_dir = os.path.join(BUCKET_URI, train_job_name)\n",
|
||||
"label_map, num_classes = get_label_map(label_map_path)\n",
|
||||
"\n",
|
||||
"# Uploads pretained checkpoint to GCS bucket.\n",
|
||||
"init_checkpoint = f\"https://storage.googleapis.com/tf_model_garden/vision/movinet/{model_name}_with_backbone.tar.gz\"\n",
|
||||
"init_checkpoint = upload_checkpoint_to_gcs(init_checkpoint)\n",
|
||||
"\n",
|
||||
"# Uploads config file according to model_id and streaming options.\n",
|
||||
"config_file = f\"{model_id}_stream\" if is_stream else model_id\n",
|
||||
"config_file = f\"https://raw.githubusercontent.com/tensorflow/models/master/official/projects/movinet/configs/yaml/movinet_{config_file}_gpu.yaml\"\n",
|
||||
"config_file = upload_config_to_gcs(config_file)\n",
|
||||
"\n",
|
||||
"# The parameters here are mainly for demonstration purpose. Please update them\n",
|
||||
"# for better performance.\n",
|
||||
"trainer_args = {\n",
|
||||
" \"experiment\": \"movinet_kinetics600\",\n",
|
||||
" \"config_file\": config_file,\n",
|
||||
" \"input_train_data_path\": input_train_data_path,\n",
|
||||
" \"input_validation_data_path\": input_validation_data_path,\n",
|
||||
" \"init_checkpoint\": init_checkpoint,\n",
|
||||
" \"model_dir\": model_dir,\n",
|
||||
" \"num_classes\": num_classes,\n",
|
||||
" \"global_batch_size\": 4,\n",
|
||||
" \"prefetch_buffer_size\": 8,\n",
|
||||
" \"shuffle_buffer_size\": 32,\n",
|
||||
" \"train_steps\": 2000,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAIN_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAIN_ACCELERATOR_TYPE,\n",
|
||||
" # Each training job uses TRAIN_NUM_GPU GPUs.\n",
|
||||
" \"accelerator_count\": TRAIN_NUM_GPU,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAIN_CONTAINER_URI,\n",
|
||||
" \"args\": [\n",
|
||||
" \"--mode=train_and_eval\",\n",
|
||||
" \"--params_override=runtime.num_gpus=%d\" % TRAIN_NUM_GPU,\n",
|
||||
" ]\n",
|
||||
" + [\"--{}={}\".format(k, v) for k, v in trainer_args.items()],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"metric_spec = {\"model_performance\": \"maximize\"}\n",
|
||||
"\n",
|
||||
"# These learning rates might not be optimal for your selected model type; To\n",
|
||||
"# tune learning rates, try hpt.DoubleParameterSpec with more trials.\n",
|
||||
"LEARNING_RATES = [1e-3, 3e-3]\n",
|
||||
"MAX_TRIAL_COUNT = len(LEARNING_RATES)\n",
|
||||
"parameter_spec = {\n",
|
||||
" \"learning_rate\": hpt.DiscreteParameterSpec(values=LEARNING_RATES, scale=\"linear\"),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"print(worker_pool_specs, metric_spec, parameter_spec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"#### Run the hyperparameter tuning job\n",
|
||||
"* `max_trial_count`: Sets an upper bound on the number of trials the service will run. The recommended practice is to start with a smaller number of trials and get a sense of how impactful your chosen hyperparameters are before scaling up.\n",
|
||||
"\n",
|
||||
"* `parallel_trial_count`: If you use parallel trials, the service provisions multiple training processing clusters. The worker pool spec that you specify when creating the job is used for each individual training cluster. Increasing the number of parallel trials reduces the amount of time the hyperparameter tuning job takes to run; however, it can reduce the effectiveness of the job overall. This is because the default tuning strategy uses results of previous trials to inform the assignment of values in subsequent trials.\n",
|
||||
"\n",
|
||||
"* `search_algorithm`: The available search algorithms are grid, random, or default (None). The default option applies Bayesian optimization to search the space of possible hyperparameter values and is the recommended algorithm.\n",
|
||||
"\n",
|
||||
"Click on the generated link in the output to see your run in the Cloud Console."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"train_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_hpt_job = aiplatform.HyperparameterTuningJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" custom_job=train_custom_job,\n",
|
||||
" metric_spec=metric_spec,\n",
|
||||
" parameter_spec=parameter_spec,\n",
|
||||
" max_trial_count=MAX_TRIAL_COUNT,\n",
|
||||
" parallel_trial_count=MAX_TRIAL_COUNT,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" search_algorithm=None,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_hpt_job.run()\n",
|
||||
"\n",
|
||||
"print(\"model_dir is:\", model_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "vugUfJEC2HrK"
|
||||
},
|
||||
"source": [
|
||||
"### Export model in Tensorflow SavedModel format"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "09Rz1AYspK19"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This job will export models from TF checkpoints to TF saved model format.\n",
|
||||
"# model_dir is from the section above.\n",
|
||||
"best_trial_dir, best_trial_evaluation_results = get_best_trial(\n",
|
||||
" model_dir, MAX_TRIAL_COUNT, EVALUATION_METRIC\n",
|
||||
")\n",
|
||||
"best_checkpoint_path = find_checkpoint_in_dir(f\"{best_trial_dir}/best_ckpt/\")\n",
|
||||
"print(\"best_trial_dir: \", best_trial_dir)\n",
|
||||
"print(\"best_trial_evaluation_results: \", best_trial_evaluation_results)\n",
|
||||
"print(\"best_checkpoint: \", best_checkpoint_path)\n",
|
||||
"\n",
|
||||
"container_args = {\n",
|
||||
" \"export_path\": f\"{model_dir}/best_model\",\n",
|
||||
" \"model_id\": model_id,\n",
|
||||
" \"num_classes\": num_classes,\n",
|
||||
" \"causal\": is_stream,\n",
|
||||
" \"checkpoint_path\": best_checkpoint_path,\n",
|
||||
" \"assert_checkpoint_objects_matched\": False,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"if is_stream:\n",
|
||||
" container_args.update(\n",
|
||||
" {\n",
|
||||
" \"conv_type\": \"2plus1d\",\n",
|
||||
" \"se_type\": \"2plus3d\",\n",
|
||||
" \"activation\": \"hard_swish\",\n",
|
||||
" \"gating_activation\": \"hard_sigmoid\",\n",
|
||||
" \"use_positional_encoding\": model_id in {\"a3\", \"a4\", \"a5\"},\n",
|
||||
" }\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": EXPORT_MACHINE_TYPE,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": EXPORT_CONTAINER_URI,\n",
|
||||
" \"args\": [\"--{}={}\".format(k, v) for k, v in container_args.items()],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"model_export_job_name = get_job_name_with_datetime(EXPORT_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"model_export_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=model_export_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"model_export_custom_job.run()\n",
|
||||
"\n",
|
||||
"print(\"best model is saved to: \", container_args[\"export_path\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"## Test trained models\n",
|
||||
"This section shows the way to test with trained models.\n",
|
||||
"1. Upload and deploy models to the [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction)\n",
|
||||
"2. Run batch predictions\n",
|
||||
"\n",
|
||||
"**Note:** The prediction container only works with the base model. If you trained a streaming model, download the model from the exported path and refer to the [MoViNet official guide](https://github.com/tensorflow/models/blob/master/official/projects/movinet/movinet_streaming_model_training_and_inference.ipynb) for running predictions locally."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gdlca3BOypXU"
|
||||
},
|
||||
"source": [
|
||||
"### Upload model to Vertex AI Model Registry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"upload_job_name = get_job_name_with_datetime(f\"{UPLOAD_JOB_PREFIX}_{model_name}\")\n",
|
||||
"\n",
|
||||
"serving_env = {\n",
|
||||
" \"MODEL_PATH\": container_args[\"export_path\"],\n",
|
||||
" \"BATCH_SIZE\": 1, # Select a larger batch size to accelerate GPU prediction.\n",
|
||||
" \"NUM_FRAMES\": 32,\n",
|
||||
" \"FPS\": output_fps,\n",
|
||||
" \"OVERLAP_FRAMES\": 24,\n",
|
||||
" \"OBJECTIVE\": OBJECTIVE,\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=PREDICTION_CONTAINER_URI,\n",
|
||||
" serving_container_ports=[PREDICTION_PORT],\n",
|
||||
" serving_container_predict_route=\"/predict\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"model.wait()\n",
|
||||
"\n",
|
||||
"print(\"The uploaded model name is: \", model_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9SZsKGeS3x6S"
|
||||
},
|
||||
"source": [
|
||||
"### Run batch predictions\n",
|
||||
"\n",
|
||||
"We will now run batch predictions with the trained MoViNet clip classification model with [Vertex AI Batch Prediction](https://cloud.google.com/vertex-ai/docs/predictions/get-batch-predictions).\n",
|
||||
"\n",
|
||||
"Please prepare an input JSONL file where each line follows [this format](https://cloud.google.com/vertex-ai/docs/video-data/classification/get-predictions?hl=en#input_data_requirements) and store it in a Cloud Storage bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vbIW9me1F2RY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Path to the prediction input JSONL file.\n",
|
||||
"test_jsonl_path = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"predict_job_name = get_job_name_with_datetime(f\"{PREDICTION_JOB_PREFIX}_{model_name}\")\n",
|
||||
"predict_destination_prefix = os.path.join(STAGING_BUCKET, predict_job_name)\n",
|
||||
"\n",
|
||||
"batch_prediction_job = model.batch_predict(\n",
|
||||
" job_display_name=predict_job_name,\n",
|
||||
" gcs_source=test_jsonl_path,\n",
|
||||
" gcs_destination_prefix=predict_destination_prefix,\n",
|
||||
" machine_type=PREDICTION_MACHINE_TYPE,\n",
|
||||
" accelerator_count=PREDICTION_ACCELERATOR_COUNT,\n",
|
||||
" accelerator_type=PREDICTION_ACCELERATOR_TYPE,\n",
|
||||
" max_replica_count=1,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"batch_prediction_job.wait()\n",
|
||||
"\n",
|
||||
"print(batch_prediction_job.display_name)\n",
|
||||
"print(batch_prediction_job.resource_name)\n",
|
||||
"print(batch_prediction_job.state)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ik-XPjfx9OCE"
|
||||
},
|
||||
"source": [
|
||||
"You can then read the prediction response JSONL files in the output directory:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "tdkW9e5B9OU1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"# The label map file was generated from the section above (`Prepare input data for training`).\n",
|
||||
"for file in tf.io.gfile.glob(os.path.join(predict_destination_prefix, \"*/*\")):\n",
|
||||
" with tf.io.gfile.GFile(file, \"r\") as f:\n",
|
||||
" for line in f:\n",
|
||||
" print_response_instance(line, label_map)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete the trained model.\n",
|
||||
"model.delete()\n",
|
||||
"# Delete custom and hpt jobs.\n",
|
||||
"if data_converter_custom_job.list(filter=f'display_name=\"{data_converter_job_name}\"'):\n",
|
||||
" data_converter_custom_job.delete()\n",
|
||||
"if train_hpt_job.list(filter=f'display_name=\"{train_job_name}\"'):\n",
|
||||
" train_hpt_job.delete()\n",
|
||||
"if model_export_custom_job.list(filter=f'display_name=\"{model_export_job_name}\"'):\n",
|
||||
" model_export_custom_job.delete()\n",
|
||||
"if batch_prediction_job.list(filter=f'display_name=\"{predict_job_name}\"'):\n",
|
||||
" batch_prediction_job.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_movinet_clip_classification.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,852 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "copyright"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "title:generic,gcp"
|
||||
},
|
||||
"source": [
|
||||
"# Get started with Model Garden Pipeline Templates for BERT models\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_template_pipelines_bert.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_template_pipelines_bert.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/communitymodel_garden/model_garden_template_pipelines_bert.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "overview:mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This tutorial demonstrates how to modify, compile and execute a prebuilt Vertex AI Model Garden pipeline template with Vertex AI Pipelines.\n",
|
||||
"\n",
|
||||
"Learn more about [Create a pipeline template](https://cloud.google.com/vertex-ai/docs/pipelines/create-pipeline-template)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "objective:mlops,stage4,get_started_vertex_model_evaluation"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn how to use a prebuilt pipeline template with `Vertex AI Pipelines` to fine-tune a BERT text classification model, where the model is accessed from `Vertex AI Model Garden`.\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `Vertex AI Pipelines`\n",
|
||||
"- `Vertex AI Training`\n",
|
||||
"- `Vertex AI Model Garden`\n",
|
||||
"- `Google Cloud Pipeline Components`\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Create a user-defined repository in the `Artifact Registry`.\n",
|
||||
"- Upload the prebuilt pipeline template to the `Artifact Registry`.\n",
|
||||
"- Create a pipeline job with the prebuilt pipeline template to fine-tune a BERT model.\n",
|
||||
"- Execute the pipeline using `Vertex AI Pipelines`.\n",
|
||||
" - Load BERT model from Vertex AI Model Garden\n",
|
||||
" - Fine-tune train the model\n",
|
||||
" - Do batch prediction\n",
|
||||
" - Evaluate the model from the batch prediction results\n",
|
||||
"- Obtain the Vertex AI Model resource from the pipeline artifacts.\n",
|
||||
"- Deploy the model to a Vertex AI Endpoint\n",
|
||||
"- Make a prediction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dataset:bank,lbn"
|
||||
},
|
||||
"source": [
|
||||
"### Model\n",
|
||||
"\n",
|
||||
"This tutorial uses a pre-trained BERT text classification model from `Vertex AI Model Garden`, which is then fine-tuned (transfer learning) on a dataset of text phrases which are classified as either FirstClass or SecondClass.\n",
|
||||
"\n",
|
||||
"Learn more about [BERT pretrained encoder model]( https://tfhub.dev/tensorflow/bert_en_uncased_L-12_H-768_A-12/3). "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "costs"
|
||||
},
|
||||
"source": [
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"* Dataflow\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and [Dataflow pricing](https://cloud.google.com/dataflow/pricing)\n",
|
||||
"and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Installations\n",
|
||||
"\n",
|
||||
"Install the packages required for executing this notebook.\n",
|
||||
"\n",
|
||||
"*Note:* This tutorial requires KFP 2.x."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform \\\n",
|
||||
" google-cloud-pipeline-components \\\n",
|
||||
" kfp==2.0.0b15"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only: Uncomment the following cell to restart the kernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "D-ZBOjErv5mM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Automatically restart kernel after installs so that your environment can access the new packages\n",
|
||||
"# import IPython\n",
|
||||
"\n",
|
||||
"# app = IPython.Application.instance()\n",
|
||||
"# app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "before_you_begin"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "before_you_begin:nogpu"
|
||||
},
|
||||
"source": [
|
||||
"### Set your project ID\n",
|
||||
"\n",
|
||||
"**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": "set_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c4ccf556d4ea"
|
||||
},
|
||||
"source": [
|
||||
"### Enable APIs\n",
|
||||
"\n",
|
||||
"You can enable the required APIs using `gcloud`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "619529337e6d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud services enable compute.googleapis.com \\\n",
|
||||
" containerregistry.googleapis.com \\\n",
|
||||
" aiplatform.googleapis.com \\\n",
|
||||
" artifactregistry.googleapis.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gcp_authenticate"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "FvQeFm3Gv5mR"
|
||||
},
|
||||
"source": [
|
||||
"**1. Vertex AI Workbench**\n",
|
||||
"* Do nothing as you are already authenticated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ce6043da7b33"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ! gcloud auth login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0367eac06a10"
|
||||
},
|
||||
"source": [
|
||||
"**3. Colab, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "21ad4dbb4a61"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"IS_COLAB = False\n",
|
||||
"# from google.colab import auth\n",
|
||||
"# auth.authenticate_user()\n",
|
||||
"# IS_COLAB=True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c13224697bfb"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bucket:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "set_service_account"
|
||||
},
|
||||
"source": [
|
||||
"#### Service Account\n",
|
||||
"\n",
|
||||
"**If you don't know your service account**, try to get your service account using `gcloud` command by executing the second cell below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"SERVICE_ACCOUNT = \"[your-service-account]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_service_account"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if (\n",
|
||||
" SERVICE_ACCOUNT == \"\"\n",
|
||||
" or SERVICE_ACCOUNT is None\n",
|
||||
" or SERVICE_ACCOUNT == \"[your-service-account]\"\n",
|
||||
"):\n",
|
||||
" # Get your service account from gcloud\n",
|
||||
" if not IS_COLAB:\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].replace(\"*\", \"\").strip()\n",
|
||||
"\n",
|
||||
" if IS_COLAB:\n",
|
||||
" shell_output = ! gcloud projects describe $PROJECT_ID\n",
|
||||
" project_number = shell_output[-1].split(\":\")[1].strip().replace(\"'\", \"\")\n",
|
||||
" SERVICE_ACCOUNT = f\"{project_number}-compute@developer.gserviceaccount.com\"\n",
|
||||
"\n",
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"source": [
|
||||
"#### Set service account access for Vertex AI Pipelines\n",
|
||||
"\n",
|
||||
"Run the following commands to grant your service account access to read and write pipeline artifacts in the bucket that you created in the previous step -- you only need to run these once per service account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectCreator \n",
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectViewer "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "setup_vars"
|
||||
},
|
||||
"source": [
|
||||
"### Set up variables\n",
|
||||
"\n",
|
||||
"Next, set up some variables used throughout the tutorial.\n",
|
||||
"### Import libraries and define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "import_kfp"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"import google.cloud.aiplatform as aiplatform\n",
|
||||
"from kfp.registry import RegistryClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=BUCKET_URI)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9b773e8d2bd2"
|
||||
},
|
||||
"source": [
|
||||
"## Create repo in Artifact Registry\n",
|
||||
"\n",
|
||||
"First, you create your own (user-defined) repository in the `Artifact Registry`. You use this repository to upload and retrieve your pipeline templates.\n",
|
||||
"\n",
|
||||
"The name of your repo is `quickstart-kfp-repo`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "520de849cee2"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REPO_NAME = \"quickstart-kfp-repo\"\n",
|
||||
"\n",
|
||||
"! gcloud artifacts repositories create {REPO_NAME} --location={REGION} --repository-format=KFP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1611d3517c0f"
|
||||
},
|
||||
"source": [
|
||||
"### Upload the pipeline template\n",
|
||||
"\n",
|
||||
"Next, you instantiate a client interface to the Artifact Registry. Then with the `upload_pipeline()` method you upload your pipeline template."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "72db37f6d67c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BERT_YAML = \"gs://cloud-samples-data/vertex-ai/dataset-management/datasets/bert_finetuning/pipeline.yaml\"\n",
|
||||
"\n",
|
||||
"! gsutil cp {BERT_YAML} pipeline.yaml\n",
|
||||
"\n",
|
||||
"client = RegistryClient(\n",
|
||||
" host=f\"https://{REGION}-kfp.pkg.dev/{PROJECT_ID}/quickstart-kfp-repo\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"templateName, versionName = client.upload_pipeline(\n",
|
||||
" file_name=\"pipeline.yaml\",\n",
|
||||
" tags=[\"v1\", \"latest\"],\n",
|
||||
" extra_headers={\n",
|
||||
" \"description\": \"This is a pipeline template for fine-tuning a BERT model.\"\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"! rm pipeline.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "02f82754fc0d"
|
||||
},
|
||||
"source": [
|
||||
"### View your artifacts in your registry\n",
|
||||
"\n",
|
||||
"Next, using the `gcloud artifacts files` command you view the artifacts, inclusive of the pipeline template, in your artifacts repository."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b2f641eb2056"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud artifacts files list --repository={REPO_NAME} --location={REGION}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9d5296831cfb"
|
||||
},
|
||||
"source": [
|
||||
"## Load and execute the pipeline job\n",
|
||||
"\n",
|
||||
"Next, you create a Vertex AI Pipeline job from your BERT pipeline template by instantiating a PipelineJob(), with the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the pipeline job.\n",
|
||||
"- `template_path`: The path to the pipeline template in the Artifact Registry.\n",
|
||||
"- `enable_caching`: On re-runs, use the results from previous successful and unchanged steps.\n",
|
||||
"- `pipeline_root`: A Cloud storage location for storing pipeline results.\n",
|
||||
"- `parameter_values`: The parameters and values that are input to the template pipeline. In this example, they are:\n",
|
||||
" - `project`: Your project ID.\n",
|
||||
" - `class_labels`: A list of valid class labels, in cardinal order.\n",
|
||||
" - `root_dir`: A Cloud Storage scratch area.\n",
|
||||
" - `training_data_path`: A Cloud Storage location to the training data.\n",
|
||||
" - `ground_truth_gcs_source_uris`: A Cloud Storage location to evaluation data."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "a3c502fc7e41"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PIPELINE_ROOT = f\"{BUCKET_URI}/pipeline_root/bert-finetuning\"\n",
|
||||
"\n",
|
||||
"job = aiplatform.PipelineJob(\n",
|
||||
" display_name=\"bert-finetuning\",\n",
|
||||
" template_path=f\"https://{REGION}-kfp.pkg.dev/{PROJECT_ID}/quickstart-kfp-repo/{templateName}/{versionName}\",\n",
|
||||
" pipeline_root=PIPELINE_ROOT,\n",
|
||||
" enable_caching=False,\n",
|
||||
" parameter_values={\n",
|
||||
" \"project\": PROJECT_ID,\n",
|
||||
" \"class_labels\": [\"FirstClass\", \"SecondClass\", \"[UNK]\"],\n",
|
||||
" \"root_dir\": BUCKET_URI,\n",
|
||||
" \"ground_truth_gcs_source_uris\": [\n",
|
||||
" \"gs://cloud-samples-data/vertex-ai/dataset-management/datasets/bert_finetuning/wide_and_deep_trainer_container_tests_input.jsonl\"\n",
|
||||
" ],\n",
|
||||
" \"training_data_path\": \"gs://cloud-samples-data/vertex-ai/dataset-management/datasets/bert_finetuning/wide_and_deep_trainer_container_tests_input.jsonl\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view_pipleline_results:bqml"
|
||||
},
|
||||
"source": [
|
||||
"### View the pipeline results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "view_pipleline_results:bqml"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_NUMBER = job.gca_resource.name.split(\"/\")[1]\n",
|
||||
"print(PROJECT_NUMBER)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_pipeline_output(job, output_task_name):\n",
|
||||
" JOB_ID = job.name\n",
|
||||
" print(JOB_ID)\n",
|
||||
" artifact = \"\"\n",
|
||||
" for _ in range(len(job.gca_resource.job_detail.task_details)):\n",
|
||||
" TASK_ID = job.gca_resource.job_detail.task_details[_].task_id\n",
|
||||
" EXECUTE_OUTPUT = (\n",
|
||||
" PIPELINE_ROOT\n",
|
||||
" + \"/\"\n",
|
||||
" + PROJECT_NUMBER\n",
|
||||
" + \"/\"\n",
|
||||
" + JOB_ID\n",
|
||||
" + \"/\"\n",
|
||||
" + output_task_name\n",
|
||||
" + \"_\"\n",
|
||||
" + str(TASK_ID)\n",
|
||||
" + \"/executor_output.json\"\n",
|
||||
" )\n",
|
||||
" GCP_RESOURCES = (\n",
|
||||
" PIPELINE_ROOT\n",
|
||||
" + \"/\"\n",
|
||||
" + PROJECT_NUMBER\n",
|
||||
" + \"/\"\n",
|
||||
" + JOB_ID\n",
|
||||
" + \"/\"\n",
|
||||
" + output_task_name\n",
|
||||
" + \"_\"\n",
|
||||
" + str(TASK_ID)\n",
|
||||
" + \"/gcp_resources\"\n",
|
||||
" )\n",
|
||||
" EVALUATION_METRICS = (\n",
|
||||
" PIPELINE_ROOT\n",
|
||||
" + \"/\"\n",
|
||||
" + PROJECT_NUMBER\n",
|
||||
" + \"/\"\n",
|
||||
" + JOB_ID\n",
|
||||
" + \"/\"\n",
|
||||
" + output_task_name\n",
|
||||
" + \"_\"\n",
|
||||
" + str(TASK_ID)\n",
|
||||
" + \"/evaluation_metrics\"\n",
|
||||
" )\n",
|
||||
" # Check if file exists, 0 is success\n",
|
||||
" !gsutil -q stat $EXECUTE_OUTPUT\n",
|
||||
" if _exit_code == 0:\n",
|
||||
" ! gsutil cat $EXECUTE_OUTPUT\n",
|
||||
" artifact = EXECUTE_OUTPUT\n",
|
||||
" break\n",
|
||||
" !gsutil -q stat $GCP_RESOURCES\n",
|
||||
" if _exit_code == 0:\n",
|
||||
" ! gsutil cat $GCP_RESOURCES\n",
|
||||
" artifact = GCP_RESOURCES\n",
|
||||
" break\n",
|
||||
" !gsutil -q stat $EVALUATION_METRICS\n",
|
||||
" if _exit_code == 0:\n",
|
||||
" ! gsutil cat $EVALUATION_METRICS\n",
|
||||
" artifact = EVALUATION_METRICS\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" return artifact\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(\"get-vertex-model\")\n",
|
||||
"artifacts = print_pipeline_output(job, \"get-vertex-model\")\n",
|
||||
"output = !gsutil cat $artifacts\n",
|
||||
"print(output)\n",
|
||||
"output = json.loads(output[0])\n",
|
||||
"model_id = output[\"artifacts\"][\"model\"][\"artifacts\"][0][\"metadata\"][\"resourceName\"]\n",
|
||||
"print(\"\\n\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f431a9e6f025"
|
||||
},
|
||||
"source": [
|
||||
"### Delete the pipeline job\n",
|
||||
"\n",
|
||||
"The method 'delete()' will delete the pipeline job."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "00bf554abbc6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"job.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3d183db57ae2"
|
||||
},
|
||||
"source": [
|
||||
"### Deploy the model\n",
|
||||
"\n",
|
||||
"Next, you deploy the model to an endpoint:\n",
|
||||
"\n",
|
||||
"- Use the `model_id` obtained from the pipeline artifacts to instaniate a Vertex AI Model resource instance.\n",
|
||||
"- Deploy the Vertex AI Model resource to a Vertex AI Endpoint resource.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "591ccc049ce5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = aiplatform.Model(model_id)\n",
|
||||
"endpoint = model.deploy(\n",
|
||||
" accelerator_count=1,\n",
|
||||
" accelerator_type=aiplatform.gapic.AcceleratorType.NVIDIA_TESLA_T4.name,\n",
|
||||
" machine_type=\"n1-standard-4\",\n",
|
||||
")\n",
|
||||
"print(endpoint)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edb781a92864"
|
||||
},
|
||||
"source": [
|
||||
"### Make a prediction\n",
|
||||
"\n",
|
||||
"Finally, you make a prediction with the deployed model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "72d94012a987"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"endpoint.predict([\"this is a test\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"# Cleaning up\n",
|
||||
"\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",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"delete_bucket = False\n",
|
||||
"\n",
|
||||
"endpoint.undeploy_all()\n",
|
||||
"endpoint.delete()\n",
|
||||
"model.delete()\n",
|
||||
"\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil rm -r $BUCKET_URI\n",
|
||||
"\n",
|
||||
"! rm -rf custom custom.tar.gz"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pipeline_templates_bert.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,850 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "copyright"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "title:generic,gcp"
|
||||
},
|
||||
"source": [
|
||||
"# Get started with Model Garden Pipeline Templates for T5X models\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_template_pipelines_t5x.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_template_pipelines_t5x.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/communitymodel_garden/model_garden_template_pipelines_t5x.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "overview:mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"This tutorial demonstrates how to modify, compile and execute a prebuilt Vertex AI Model Garden pipeline template with Vertex AI Pipelines.\n",
|
||||
"\n",
|
||||
"Learn more about [Create a pipeline template](https://cloud.google.com/vertex-ai/docs/pipelines/create-pipeline-template)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "objective:mlops,stage4,get_started_vertex_model_evaluation"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn how to use a prebuilt pipeline template with `Vertex AI Pipelines` to fine-tune a T5X text classification model, where the model is accessed from `Vertex AI Model Garden`.\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services:\n",
|
||||
"\n",
|
||||
"- `Vertex AI Pipelines`\n",
|
||||
"- `Vertex AI Training`\n",
|
||||
"- `Vertex AI Model Garden`\n",
|
||||
"- `Google Cloud Pipeline Components`\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"- Create a user-defined repository in the `Artifact Registry`.\n",
|
||||
"- Upload the prebuilt pipeline template to the `Artifact Registry`.\n",
|
||||
"- Create a pipeline job with the prebuilt pipeline template to fine-tune a T5X model.\n",
|
||||
"- Execute the pipeline using `Vertex AI Pipelines`.\n",
|
||||
" - Load T5X model from Vertex AI Model Garden\n",
|
||||
" - Fine-tune train the model\n",
|
||||
"- Obtain the Vertex AI Model resource from the pipeline artifacts.\n",
|
||||
"- Deploy the model to a Vertex AI Endpoint\n",
|
||||
"- Make a prediction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dataset:bank,lbn"
|
||||
},
|
||||
"source": [
|
||||
"### Model\n",
|
||||
"\n",
|
||||
"This tutorial uses a pre-trained T5 text classification model from `Vertex AI Model Garden`, which is then fine-tuned (transfer learning) on a dataset of text phrases which are classified as either FirstClass or SecondClass.\n",
|
||||
"\n",
|
||||
"Learn more about [Text-to-text transfer transformer](https://github.com/google-research/text-to-text-transfer-transformer). "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "costs"
|
||||
},
|
||||
"source": [
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"* Dataflow\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing), [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and [Dataflow pricing](https://cloud.google.com/dataflow/pricing)\n",
|
||||
"and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"source": [
|
||||
"## Installations\n",
|
||||
"\n",
|
||||
"Install the packages required for executing this notebook.\n",
|
||||
"\n",
|
||||
"*Note:* This tutorial requires KFP 2.x."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "install_mlops"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform \\\n",
|
||||
" google-cloud-pipeline-components \\\n",
|
||||
" kfp==2.0.0b15"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "restart"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only: Uncomment the following cell to restart the kernel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "D-ZBOjErv5mM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Automatically restart kernel after installs so that your environment can access the new packages\n",
|
||||
"# import IPython\n",
|
||||
"\n",
|
||||
"# app = IPython.Application.instance()\n",
|
||||
"# app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "before_you_begin"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "before_you_begin:nogpu"
|
||||
},
|
||||
"source": [
|
||||
"### Set your project ID\n",
|
||||
"\n",
|
||||
"**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": "set_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c4ccf556d4ea"
|
||||
},
|
||||
"source": [
|
||||
"### Enable APIs\n",
|
||||
"\n",
|
||||
"You can enable the required APIs using `gcloud`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "619529337e6d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud services enable compute.googleapis.com \\\n",
|
||||
" containerregistry.googleapis.com \\\n",
|
||||
" aiplatform.googleapis.com \\\n",
|
||||
" artifactregistry.googleapis.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gcp_authenticate"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "FvQeFm3Gv5mR"
|
||||
},
|
||||
"source": [
|
||||
"**1. Vertex AI Workbench**\n",
|
||||
"* Do nothing as you are already authenticated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ce6043da7b33"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ! gcloud auth login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0367eac06a10"
|
||||
},
|
||||
"source": [
|
||||
"**3. Colab, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "21ad4dbb4a61"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"IS_COLAB = False\n",
|
||||
"# from google.colab import auth\n",
|
||||
"# auth.authenticate_user()\n",
|
||||
"# IS_COLAB=True"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c13224697bfb"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bucket:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_URI = f\"gs://your-bucket-name-{PROJECT_ID}-unique\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"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": "set_service_account"
|
||||
},
|
||||
"source": [
|
||||
"#### Service Account\n",
|
||||
"\n",
|
||||
"**If you don't know your service account**, try to get your service account using `gcloud` command by executing the second cell below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"SERVICE_ACCOUNT = \"[your-service-account]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_service_account"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if (\n",
|
||||
" SERVICE_ACCOUNT == \"\"\n",
|
||||
" or SERVICE_ACCOUNT is None\n",
|
||||
" or SERVICE_ACCOUNT == \"[your-service-account]\"\n",
|
||||
"):\n",
|
||||
" # Get your service account from gcloud\n",
|
||||
" if not IS_COLAB:\n",
|
||||
" shell_output = !gcloud auth list 2>/dev/null\n",
|
||||
" SERVICE_ACCOUNT = shell_output[2].replace(\"*\", \"\").strip()\n",
|
||||
"\n",
|
||||
" if IS_COLAB:\n",
|
||||
" shell_output = ! gcloud projects describe $PROJECT_ID\n",
|
||||
" project_number = shell_output[-1].split(\":\")[1].strip().replace(\"'\", \"\")\n",
|
||||
" SERVICE_ACCOUNT = f\"{project_number}-compute@developer.gserviceaccount.com\"\n",
|
||||
"\n",
|
||||
" print(\"Service Account:\", SERVICE_ACCOUNT)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"source": [
|
||||
"#### Set service account access for Vertex AI Pipelines\n",
|
||||
"\n",
|
||||
"Run the following commands to grant your service account access to read and write pipeline artifacts in the bucket that you created in the previous step -- you only need to run these once per service account."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_service_account:pipelines"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectCreator \n",
|
||||
"! gsutil iam ch serviceAccount:{SERVICE_ACCOUNT}:roles/storage.objectViewer "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "setup_vars"
|
||||
},
|
||||
"source": [
|
||||
"### Set up variables\n",
|
||||
"\n",
|
||||
"Next, set up some variables used throughout the tutorial.\n",
|
||||
"### Import libraries and define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "import_kfp"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"\n",
|
||||
"import google.cloud.aiplatform as aiplatform\n",
|
||||
"from kfp.registry import RegistryClient"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI SDK for Python\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "init_aip:mbsdk,all"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=BUCKET_URI)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9b773e8d2bd2"
|
||||
},
|
||||
"source": [
|
||||
"## Create repo in Artifact Registry\n",
|
||||
"\n",
|
||||
"First, you create your own (user-defined) repository in the `Artifact Registry`. You use this repository to upload and retreive your pipeline templates.\n",
|
||||
"\n",
|
||||
"The name of your repo is `quickstart-kfp-repo`"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "520de849cee2"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REPO_NAME = \"quickstart-kfp-repo\"\n",
|
||||
"\n",
|
||||
"! gcloud artifacts repositories create {REPO_NAME} --location={REGION} --repository-format=KFP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1611d3517c0f"
|
||||
},
|
||||
"source": [
|
||||
"### Upload the pipeline template\n",
|
||||
"\n",
|
||||
"Next, you instantiate a client interface to the Artifact Registry. Then with the `upload_pipeline()` method you upload your pipeline template."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7f002e57998a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"T5X_YAML = \"gs://cloud-samples-data/vertex-ai/dataset-management/datasets/t5_finetuning/pipeline.yaml\"\n",
|
||||
"\n",
|
||||
"! gsutil cp {T5X_YAML} pipeline.yaml\n",
|
||||
"\n",
|
||||
"client = RegistryClient(\n",
|
||||
" host=f\"https://{REGION}-kfp.pkg.dev/{PROJECT_ID}/quickstart-kfp-repo\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"templateName, versionName = client.upload_pipeline(\n",
|
||||
" file_name=\"pipeline.yaml\",\n",
|
||||
" tags=[\"v1\", \"latest\"],\n",
|
||||
" extra_headers={\n",
|
||||
" \"description\": \"This is a pipeline template for fine-tuning a T5 model.\"\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"! rm pipeline.yaml"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "02f82754fc0d"
|
||||
},
|
||||
"source": [
|
||||
"### View your artifacts in your registry\n",
|
||||
"\n",
|
||||
"Next, using the `gcloud artifacts files` command you view the artifacts, inclusive of the pipeline template, in your artifacts repository."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b2f641eb2056"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud artifacts files list --repository={REPO_NAME} --location={REGION}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "968a46a3cb6d"
|
||||
},
|
||||
"source": [
|
||||
"## Load and execute the pipeline job\n",
|
||||
"\n",
|
||||
"Next, you create a Vertex AI Pipeline job from your T5 pipeline template by instantiating a PipelineJob(), with the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the pipeline job.\n",
|
||||
"- `template_path`: The path to the pipeline template in the Artifact Registry.\n",
|
||||
"- `enable_caching`: On re-runs, use the results from previous successful and unchanged steps.\n",
|
||||
"- `pipeline_root`: A Cloud storage location for storing pipeline results.\n",
|
||||
"- `parameter_values`: The parameters and values that are input to the template pipeline. In this example, they are:\n",
|
||||
"TODO\n",
|
||||
" - `project`: Your project ID.\n",
|
||||
" - `class_labels`: A list of valid class labels, in cardinal order.\n",
|
||||
" - `root_dir`: A Cloud Storage scratch area.\n",
|
||||
" - `training_data_path`: A Cloud Storage location to the training data.\n",
|
||||
" - `ground_truth_gcs_source_uris`: A Cloud Storage location to evaluation data."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "a3c502fc7e41"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PIPELINE_ROOT = f\"{BUCKET_URI}/pipeline_root/t5_finetuning\"\n",
|
||||
"\n",
|
||||
"job = aiplatform.PipelineJob(\n",
|
||||
" display_name=\"t5x-finetuning\",\n",
|
||||
" template_path=f\"https://{REGION}-kfp.pkg.dev/{PROJECT_ID}/quickstart-kfp-repo/{templateName}/{versionName}\",\n",
|
||||
" pipeline_root=PIPELINE_ROOT,\n",
|
||||
" enable_caching=False,\n",
|
||||
" parameter_values={\n",
|
||||
" \"project_id\": PROJECT_ID,\n",
|
||||
" \"accelerator_count\": 32,\n",
|
||||
" \"feature_keys\": \"question\",\n",
|
||||
" \"label_key\": \"answer\",\n",
|
||||
" \"training_data_path\": \"gs://cloud-llm-public/tfds/natural_questions_open/1.0.0_shortened/natural_questions_open-train.tfrecord-00000-of-00001\",\n",
|
||||
" \"validation_data_path\": \"gs://cloud-llm-public/tfds/natural_questions_open/1.0.0_shortened/natural_questions_open-validation.tfrecord-00000-of-00001\",\n",
|
||||
" },\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "view_pipleline_results:bqml"
|
||||
},
|
||||
"source": [
|
||||
"### View the pipeline results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "view_pipleline_results:bqml"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_NUMBER = job.gca_resource.name.split(\"/\")[1]\n",
|
||||
"print(PROJECT_NUMBER)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def print_pipeline_output(job, output_task_name):\n",
|
||||
" JOB_ID = job.name\n",
|
||||
" print(JOB_ID)\n",
|
||||
" artifact = \"\"\n",
|
||||
" for _ in range(len(job.gca_resource.job_detail.task_details)):\n",
|
||||
" TASK_ID = job.gca_resource.job_detail.task_details[_].task_id\n",
|
||||
" EXECUTE_OUTPUT = (\n",
|
||||
" PIPELINE_ROOT\n",
|
||||
" + \"/\"\n",
|
||||
" + PROJECT_NUMBER\n",
|
||||
" + \"/\"\n",
|
||||
" + JOB_ID\n",
|
||||
" + \"/\"\n",
|
||||
" + output_task_name\n",
|
||||
" + \"_\"\n",
|
||||
" + str(TASK_ID)\n",
|
||||
" + \"/executor_output.json\"\n",
|
||||
" )\n",
|
||||
" GCP_RESOURCES = (\n",
|
||||
" PIPELINE_ROOT\n",
|
||||
" + \"/\"\n",
|
||||
" + PROJECT_NUMBER\n",
|
||||
" + \"/\"\n",
|
||||
" + JOB_ID\n",
|
||||
" + \"/\"\n",
|
||||
" + output_task_name\n",
|
||||
" + \"_\"\n",
|
||||
" + str(TASK_ID)\n",
|
||||
" + \"/gcp_resources\"\n",
|
||||
" )\n",
|
||||
" EVALUATION_METRICS = (\n",
|
||||
" PIPELINE_ROOT\n",
|
||||
" + \"/\"\n",
|
||||
" + PROJECT_NUMBER\n",
|
||||
" + \"/\"\n",
|
||||
" + JOB_ID\n",
|
||||
" + \"/\"\n",
|
||||
" + output_task_name\n",
|
||||
" + \"_\"\n",
|
||||
" + str(TASK_ID)\n",
|
||||
" + \"/evaluation_metrics\"\n",
|
||||
" )\n",
|
||||
" # Check if file exists, 0 is success\n",
|
||||
" !gsutil -q stat $EXECUTE_OUTPUT\n",
|
||||
" if _exit_code == 0:\n",
|
||||
" ! gsutil cat $EXECUTE_OUTPUT\n",
|
||||
" artifact = EXECUTE_OUTPUT\n",
|
||||
" break\n",
|
||||
" !gsutil -q stat $GCP_RESOURCES\n",
|
||||
" if _exit_code == 0:\n",
|
||||
" ! gsutil cat $GCP_RESOURCES\n",
|
||||
" artifact = GCP_RESOURCES\n",
|
||||
" break\n",
|
||||
" !gsutil -q stat $EVALUATION_METRICS\n",
|
||||
" if _exit_code == 0:\n",
|
||||
" ! gsutil cat $EVALUATION_METRICS\n",
|
||||
" artifact = EVALUATION_METRICS\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" return artifact\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"print(\"model-upload\")\n",
|
||||
"artifacts = print_pipeline_output(job, \"model-upload\")\n",
|
||||
"output = !gsutil cat $artifacts\n",
|
||||
"print(output)\n",
|
||||
"output = json.loads(output[0])\n",
|
||||
"model_id = output[\"artifacts\"][\"model\"][\"artifacts\"][0][\"metadata\"][\"resourceName\"]\n",
|
||||
"print(\"\\n\\n\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f431a9e6f025"
|
||||
},
|
||||
"source": [
|
||||
"### Delete the pipeline job\n",
|
||||
"\n",
|
||||
"The method 'delete()' will delete the pipeline job."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "00bf554abbc6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"job.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3d183db57ae2"
|
||||
},
|
||||
"source": [
|
||||
"### Deploy the model\n",
|
||||
"\n",
|
||||
"Next, you deploy the model to an endpoint:\n",
|
||||
"\n",
|
||||
"- Use the `model_id` obtained from the pipeline artifacts to instantiate a Vertex AI Model resource instance.\n",
|
||||
"- Deploy the Vertex AI Model resource to a Vertex AI Endpoint resource.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "591ccc049ce5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = aiplatform.Model(model_id)\n",
|
||||
"endpoint = model.deploy(\n",
|
||||
" accelerator_count=1,\n",
|
||||
" accelerator_type=aiplatform.gapic.AcceleratorType.NVIDIA_TESLA_T4.name,\n",
|
||||
" machine_type=\"n1-standard-4\",\n",
|
||||
")\n",
|
||||
"print(endpoint)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "edb781a92864"
|
||||
},
|
||||
"source": [
|
||||
"### Make a prediction\n",
|
||||
"\n",
|
||||
"Finally, you make a prediction with the deployed model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "72d94012a987"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"endpoint.predict([\"this is a test\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"source": [
|
||||
"# Cleaning up\n",
|
||||
"\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",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cleanup:mbsdk"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"delete_bucket = True\n",
|
||||
"\n",
|
||||
"endpoint.undeploy_all()\n",
|
||||
"endpoint.delete()\n",
|
||||
"model.delete()\n",
|
||||
"\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil rm -r $BUCKET_URI\n",
|
||||
"\n",
|
||||
"! rm -rf custom custom.tar.gz"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pipeline_templates_t5x.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden: Google Proprietary Model Image Classification\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_proprietary_image_classification.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_proprietary_image_classification.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_proprietary_image_classification.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use Google proprietary image classification model training/deployment in [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models using Vertex SDK\n",
|
||||
"\n",
|
||||
"* Test trained models\n",
|
||||
" * View the trained model in [Vertex Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction)\n",
|
||||
" * Deploy uploaded models\n",
|
||||
" * Run predictions\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage.\n",
|
||||
"\n",
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset used for this tutorial is the [Flowers dataset](https://www.tensorflow.org/datasets/catalog/tf_flowers) from [TensorFlow Datasets](https://www.tensorflow.org/datasets/catalog/overview). The version of the dataset you use in this tutorial is stored in a public Cloud Storage bucket. The trained model predicts the type of flower an image is from a class of five flowers: daisy, dandelion, rose, sunflower, or tulip."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
"# Automatically restart kernel after installs\n",
|
||||
"import IPython\n",
|
||||
"\n",
|
||||
"app = IPython.Application.instance()\n",
|
||||
"app.kernel.do_shutdown(True)\n",
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"### 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 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": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# The project and bucket are for experiments below.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can choose a region from https://cloud.google.com/about/locations.\n",
|
||||
"# Only regions prefixed by \"us\", \"europe\", or \"asia\" are supported.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"europe\", or \"asia\".'\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"OBJECTIVE = \"icn\"\n",
|
||||
"\n",
|
||||
"# Dataset constants\n",
|
||||
"DATASET_PREFIX = \"dataset-icn\"\n",
|
||||
"\n",
|
||||
"# Training constants.\n",
|
||||
"TRAINING_JOB_PREFIX = \"train\"\n",
|
||||
"\n",
|
||||
"# The image classification flowers dataset used to train the model.\n",
|
||||
"DATASET_FILE = (\n",
|
||||
" \"gs://cloud-samples-data/vision/automl_classification/flowers/all_data_v2.csv\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Evaluation constants.\n",
|
||||
"EVALUATION_METRIC = \"accuracy\"\n",
|
||||
"\n",
|
||||
"# Prediction constant.\n",
|
||||
"# Model does not support dedicated deployment resources.\n",
|
||||
"# An n1-standard-4 machine with 1 P100 GPU will be used.\n",
|
||||
"DEPLOY_JOB_PREFIX = \"deploy\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZZFPe_GezXg8"
|
||||
},
|
||||
"source": [
|
||||
"### Define common libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy\n",
|
||||
"import tensorflow as tf\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_img(path):\n",
|
||||
" img = tf.io.read_file(path)\n",
|
||||
" img = tf.image.decode_jpeg(img, channels=3)\n",
|
||||
" return Image.fromarray(numpy.uint8(img)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def display_image(image):\n",
|
||||
" _ = plt.figure(figsize=(20, 15))\n",
|
||||
" plt.grid(False)\n",
|
||||
" plt.imshow(image)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_prediction_instances(test_filepath, new_width=-1):\n",
|
||||
" if new_width <= 0:\n",
|
||||
" with tf.io.gfile.GFile(test_filepath, \"rb\") as input_file:\n",
|
||||
" encoded_string = base64.b64encode(input_file.read()).decode(\"utf-8\")\n",
|
||||
" else:\n",
|
||||
" img = load_img(test_filepath)\n",
|
||||
" width, height = img.size\n",
|
||||
" print(\"original input image size: \", width, \" , \", height)\n",
|
||||
" new_height = int(height * new_width / width)\n",
|
||||
" new_img = img.resize((new_width, new_height))\n",
|
||||
" print(\"resized input image size: \", new_width, \" , \", new_height)\n",
|
||||
" buffered = BytesIO()\n",
|
||||
" new_img.save(buffered, format=\"JPEG\")\n",
|
||||
" encoded_string = base64.b64encode(buffered.getvalue()).decode(\"utf-8\")\n",
|
||||
"\n",
|
||||
" instances = [{\"content\": encoded_string}]\n",
|
||||
" return instances"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Q149N3V6Uynm"
|
||||
},
|
||||
"source": [
|
||||
"## Create a dataset\n",
|
||||
"\n",
|
||||
"This tutorial uses a version of the Flowers dataset that is stored in a public Cloud Storage bucket, using a CSV index file.\n",
|
||||
"\n",
|
||||
"Start by doing a quick peek at the data. You count the number of examples by counting the number of rows in the CSV index file (`wc -l`) and then peek at the first few rows."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "yR4x-5XiVIlv"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"count = ! gsutil cat $DATASET_FILE | wc -l\n",
|
||||
"print(\"Number of Examples\", int(count[0]))\n",
|
||||
"\n",
|
||||
"print(\"First 10 rows\")\n",
|
||||
"! gsutil cat $DATASET_FILE | head"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8yfBZ1_8VZvq"
|
||||
},
|
||||
"source": [
|
||||
"Next, create the `Dataset` resource using the `create` method for the `ImageDataset` class, which takes the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the `Dataset` resource.\n",
|
||||
"- `gcs_source`: A list of one or more dataset index files to import the data items into the `Dataset` resource.\n",
|
||||
"- `import_schema_uri`: The data labeling schema for the data items.\n",
|
||||
"\n",
|
||||
"This operation may take several minutes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "eehB1gwHVfRh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = aiplatform.ImageDataset.create(\n",
|
||||
" display_name=DATASET_PREFIX + \"_flowers\",\n",
|
||||
" gcs_source=[DATASET_FILE],\n",
|
||||
" import_schema_uri=aiplatform.schema.dataset.ioformat.image.single_label_classification,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(dataset.resource_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RB_xY9ipr7ZU"
|
||||
},
|
||||
"source": [
|
||||
"## Train new models\n",
|
||||
"\n",
|
||||
"### Create and run training pipeline\n",
|
||||
"\n",
|
||||
"To train an AutoML model, you perform two steps:\n",
|
||||
"1. Create a training pipeline.\n",
|
||||
"2. Run the pipeline.\n",
|
||||
"\n",
|
||||
"#### Create training pipeline\n",
|
||||
"\n",
|
||||
"An AutoML training pipeline is created with the `AutoMLImageTrainingJob` class, with the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the `TrainingJob` resource.\n",
|
||||
"- `prediction_type`: The type task to train the model for.\n",
|
||||
" - `classification`: An image classification model.\n",
|
||||
" - `object_detection`: An image object detection model.\n",
|
||||
"- `multi_label`: If a classification task is single (`False`) or multi-labeled (`True`).\n",
|
||||
"- `model_type`: The type of model for deployment.\n",
|
||||
" - `EFFICIENTNET`: A model that is available in Vertex Model Garden image classification training with customizable hyperparameters. Best tailored to be used within Google Cloud, and cannot be exported externally.\n",
|
||||
" - `MAXVIT`: A model that is available in Vertex Model Garden image classification training with customizable hyperparameters. Best tailored to be used within Google Cloud, and cannot be exported externally.\n",
|
||||
"- `checkpoint_name`: Optional. The field is reserved for Model Garden model training, based on the provided pre-trained model checkpoint.\n",
|
||||
"- `trainer_config`: Optional. The field is usually used together with the Model Garden model training when passing the customized configs for the trainer.\n",
|
||||
"\n",
|
||||
" Example with all supported parameters:\n",
|
||||
"```\n",
|
||||
" trainer_config = {\n",
|
||||
" 'global_batch_size': '8',\n",
|
||||
" 'learning_rate': '0.001',\n",
|
||||
" 'optimizer_type': 'sgd',\n",
|
||||
" 'optimizer_momentum': '0.9',\n",
|
||||
" 'train_steps': '10000',\n",
|
||||
" 'accelerator_count': '1',\n",
|
||||
" }\n",
|
||||
"```\n",
|
||||
"- `metric_spec`: Dictionary representing metrics to optimize. The dictionary key is the `metric_id`, which is reported by your training job, with possible values being ('loss', 'accuracy') and the dictionary value is the optimization goal of the metric ('minimize' or 'maximize').\n",
|
||||
"For example: `metric_spec = {'loss': 'minimize', 'accuracy': 'maximize'}`\n",
|
||||
"- `parameter_spec`: Dictionary representing parameters to optimize. The dictionary key is the `metric_id`, which is passed into your training job as a command line key word argument, and the dictionary value is the parameter\n",
|
||||
"specification of the metric. Supported parameter specifications can be found in aiplatform.hyperparameter_tuning.\n",
|
||||
"```\n",
|
||||
" from google.cloud.aiplatform.aiplatform import hpt as hpt\n",
|
||||
"\n",
|
||||
" parameter_spec = {\n",
|
||||
" 'learning_rate': hpt.DoubleParameterSpec(min=1e-7, max=1, scale='linear'),\n",
|
||||
" }\n",
|
||||
"```\n",
|
||||
"- `search_algorithm`: The search algorithm specified for the Study. Accepts one of the following:\n",
|
||||
" - `None`: If you do not specify an algorithm, your job uses the default\n",
|
||||
" Vertex AI algorithm. The default algorithm applies Bayesian optimization\n",
|
||||
" to arrive at the optimal solution with a more effective search over the\n",
|
||||
" parameter space.\n",
|
||||
" - `grid`: A simple grid search within the feasible space. This option is\n",
|
||||
" particularly useful if you want to specify a quantity of trials that is greater than the number of points in the feasible space. In such cases, if you do not specify a grid search, the Vertex AI default algorithm may generate duplicate suggestions. To use grid search, all parameter specs must be of type `IntegerParameterSpec`, `CategoricalParameterSpec`, or `DiscreteParameterSpec`.\n",
|
||||
" - `random`: A simple random search within the feasible space.\n",
|
||||
"- `measurement_selection`: This indicates which measurement to use\n",
|
||||
"if/when the service automatically selects the final measurement from\n",
|
||||
"previously reported intermediate measurements.\n",
|
||||
" Accepts: `best`, `last` Choose this based on two considerations:\n",
|
||||
" - A): Do you expect your measurements to monotonically improve? If so,\n",
|
||||
" choose `last`. On the other hand, if you\\'re in a situation where\n",
|
||||
" your system can **over-train** and you expect the performance to get\n",
|
||||
" better for a while but then start declining, choose `best`.\n",
|
||||
" - B): Are your measurements significantly noisy and/or irreproducible? If\n",
|
||||
" so, `best` will tend to be over-optimistic, and it may be better\n",
|
||||
" to choose `last`. If both or neither of (A) and (B) apply, it\n",
|
||||
" doesn't matter which selection type is chosen.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "un0tyqU7We_A"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
|
||||
"\n",
|
||||
"TRAINER_CONFIG = {\n",
|
||||
" \"global_batch_size\": \"8\",\n",
|
||||
" \"learning_rate\": \"0.001\",\n",
|
||||
" \"train_steps\": \"10000\",\n",
|
||||
" \"accelerator_count\": \"1\",\n",
|
||||
"}\n",
|
||||
"METRIC_SPEC_KEY = \"accuracy\"\n",
|
||||
"METRIC_SPEC_VALUE = \"maximize\"\n",
|
||||
"SEARCH_ALGORITHM = \"random\"\n",
|
||||
"MEASUREMENT_SELECTION = \"best\"\n",
|
||||
"MODEL_TYPE = (\n",
|
||||
" \"MAXVIT\" # @param {type:\"string\"} one of the values [\"MAXVIT\", \"EFFICIENTNET\"]\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"job = aiplatform.AutoMLImageTrainingJob(\n",
|
||||
" display_name=get_job_name_with_datetime(TRAINING_JOB_PREFIX),\n",
|
||||
" prediction_type=\"classification\",\n",
|
||||
" multi_label=False,\n",
|
||||
" model_type=MODEL_TYPE,\n",
|
||||
" base_model=None,\n",
|
||||
" trainer_config=TRAINER_CONFIG,\n",
|
||||
" metric_spec={METRIC_SPEC_KEY: METRIC_SPEC_VALUE},\n",
|
||||
" parameter_spec={\n",
|
||||
" \"learning_rate\": hpt.DoubleParameterSpec(min=0.001, max=0.1, scale=\"log\"),\n",
|
||||
" },\n",
|
||||
" search_algorithm=SEARCH_ALGORITHM,\n",
|
||||
" measurement_selection=MEASUREMENT_SELECTION,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(job)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"#### Run the training pipeline\n",
|
||||
"\n",
|
||||
"Next, run the DAG to start the training job by invoking the method `run`, with the following parameters:\n",
|
||||
"\n",
|
||||
"- `dataset`: The `Dataset` resource to train the model.\n",
|
||||
"- `model_display_name`: The human readable name for the trained model.\n",
|
||||
"- `training_fraction_split`: The percentage of the dataset to use for training.\n",
|
||||
"- `validation_fraction_split`: The percentage of the dataset to use for validation.\n",
|
||||
"- `test_fraction_split`: The percentage of the dataset to use for test (holdout data).\n",
|
||||
"- `budget_milli_node_hours`: (optional) Maximum training time specified in unit of millihours (1000 = hour).\n",
|
||||
"- `disable_early_stopping`: If `True`, training may be completed before using the entire budget if the service believes it cannot further improve on the model objective measurements.\n",
|
||||
"\n",
|
||||
"The `run` method, when completed, returns the `Model` resource.\n",
|
||||
"\n",
|
||||
"The execution of the training pipeline will take up to 60 minutes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = job.run(\n",
|
||||
" dataset=dataset,\n",
|
||||
" model_display_name=get_job_name_with_datetime(\"flowers\"),\n",
|
||||
" training_fraction_split=0.8,\n",
|
||||
" validation_fraction_split=0.1,\n",
|
||||
" test_fraction_split=0.1,\n",
|
||||
" budget_milli_node_hours=8000,\n",
|
||||
" disable_early_stopping=False,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Model is: \", model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"## Test trained models\n",
|
||||
"This section shows how to test the trained models.\n",
|
||||
"1. Deploy model from Model Registry\n",
|
||||
"2. Run online predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Deploy model from Model Registry\n",
|
||||
"\n",
|
||||
"deploy_model_name = get_job_name_with_datetime(DEPLOY_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"print(\"The deployed job name is: \", deploy_model_name)\n",
|
||||
"\n",
|
||||
"endpoint = model.deploy(\n",
|
||||
" deployed_model_display_name=deploy_model_name,\n",
|
||||
" machine_type=\"\",\n",
|
||||
" traffic_split={\"0\": 100},\n",
|
||||
" accelerator_type=None,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" min_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"print(\"endpoint id is: \", endpoint_id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vbIW9me1F2RY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Run online predictions\n",
|
||||
"\n",
|
||||
"# test image file path from a GCS bucket\n",
|
||||
"test_filepath = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"with tf.io.gfile.GFile(test_filepath, \"rb\") as f:\n",
|
||||
" content = f.read()\n",
|
||||
"\n",
|
||||
"# The format of each instance should conform to the deployed model's prediction input schema.\n",
|
||||
"instances = [{\"content\": base64.b64encode(content).decode(\"utf-8\")}]\n",
|
||||
"\n",
|
||||
"prediction = endpoint.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"img = load_img(test_filepath)\n",
|
||||
"display_image(img)\n",
|
||||
"print(prediction)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.undeploy_all()\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_proprietary_image_classification.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+687
@@ -0,0 +1,687 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden: Google Proprietary Model Image Object Detection\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_proprietary_image_object_detection.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_proprietary_image_object_detection.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_proprietary_image_object_detection.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use Google proprietary image object detection model training/deployment in [Vertex AI Model Garden](https://cloud.google.com/model-garden).\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models using Vertex SDK\n",
|
||||
"\n",
|
||||
"* Test trained models\n",
|
||||
" * View the trained model in [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction)\n",
|
||||
" * Deploy uploaded models\n",
|
||||
" * Run predictions\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage.\n",
|
||||
"\n",
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset used for this tutorial is the Salads category of the [OpenImages dataset](https://www.tensorflow.org/datasets/catalog/open_images_v4) from [TensorFlow Datasets](https://www.tensorflow.org/datasets/catalog/overview). This dataset does not require any feature engineering. The version of the dataset you will use in this tutorial is stored in a public Cloud Storage bucket. The trained model predicts the bounding box locations and corresponding type of salad items in an image from a class of five items: salad, seafood, tomato, baked goods, or cheese."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
"# Automatically restart kernel after installs\n",
|
||||
"import IPython\n",
|
||||
"\n",
|
||||
"app = IPython.Application.instance()\n",
|
||||
"app.kernel.do_shutdown(True)\n",
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"### 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 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": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# The project and bucket are for experiments below.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can choose a region from https://cloud.google.com/about/locations.\n",
|
||||
"# Only regions prefixed by \"us\", \"europe\", or \"asia\" are supported.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"europe\", or \"asia\".'\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"OBJECTIVE = \"iod\"\n",
|
||||
"\n",
|
||||
"# Dataset constants.\n",
|
||||
"DATASET_PREFIX = \"dataset-iod\"\n",
|
||||
"\n",
|
||||
"# Training constants.\n",
|
||||
"TRAINING_JOB_PREFIX = \"train\"\n",
|
||||
"# The image object detection salad dataset used to train the model\n",
|
||||
"DATASET_FILE = \"gs://cloud-samples-data/vision/salads.csv\"\n",
|
||||
"\n",
|
||||
"# Evaluation constants.\n",
|
||||
"EVALUATION_METRIC = \"AP50\"\n",
|
||||
"\n",
|
||||
"# Prediction constants.\n",
|
||||
"# An n1-standard-4 machine with 1 P100 GPU will be used.\n",
|
||||
"DEPLOY_JOB_PREFIX = \"deploy\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZZFPe_GezXg8"
|
||||
},
|
||||
"source": [
|
||||
"### Define common libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy as np\n",
|
||||
"import tensorflow as tf\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image, ImageColor, ImageDraw, ImageFont\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_img(path):\n",
|
||||
" img = tf.io.read_file(path)\n",
|
||||
" img = tf.image.decode_jpeg(img, channels=3)\n",
|
||||
" return Image.fromarray(np.uint8(img)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def display_image(image):\n",
|
||||
" _ = plt.figure(figsize=(20, 15))\n",
|
||||
" plt.grid(False)\n",
|
||||
" plt.imshow(image)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def draw_bounding_box_on_image(\n",
|
||||
" image, ymin, xmin, ymax, xmax, color, font, thickness=4, display_str_list=()\n",
|
||||
"):\n",
|
||||
" \"\"\"Adds a bounding box to an image.\"\"\"\n",
|
||||
" draw = ImageDraw.Draw(image)\n",
|
||||
" im_width, im_height = image.size\n",
|
||||
" (left, right, top, bottom) = (\n",
|
||||
" xmin * im_width,\n",
|
||||
" xmax * im_width,\n",
|
||||
" ymin * im_height,\n",
|
||||
" ymax * im_height,\n",
|
||||
" )\n",
|
||||
" draw.line(\n",
|
||||
" [(left, top), (left, bottom), (right, bottom), (right, top), (left, top)],\n",
|
||||
" width=thickness,\n",
|
||||
" fill=color,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # If the total height of the display strings added to the top of the bounding\n",
|
||||
" # box exceeds the top of the image, stack the strings below the bounding box\n",
|
||||
" # instead of above.\n",
|
||||
" display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]\n",
|
||||
" # Each display_str has a top and bottom margin of 0.05x.\n",
|
||||
" total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)\n",
|
||||
"\n",
|
||||
" if top > total_display_str_height:\n",
|
||||
" text_bottom = top\n",
|
||||
" else:\n",
|
||||
" text_bottom = top + total_display_str_height\n",
|
||||
" # Reverse list and print from bottom to top.\n",
|
||||
" for display_str in display_str_list[::-1]:\n",
|
||||
" text_width, text_height = font.getsize(display_str)\n",
|
||||
" margin = np.ceil(0.05 * text_height)\n",
|
||||
" draw.rectangle(\n",
|
||||
" [\n",
|
||||
" (left, text_bottom - text_height - 2 * margin),\n",
|
||||
" (left + text_width, text_bottom),\n",
|
||||
" ],\n",
|
||||
" fill=color,\n",
|
||||
" )\n",
|
||||
" draw.text(\n",
|
||||
" (left + margin, text_bottom - text_height - margin),\n",
|
||||
" display_str,\n",
|
||||
" fill=\"black\",\n",
|
||||
" font=font,\n",
|
||||
" )\n",
|
||||
" text_bottom -= text_height - 2 * margin\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def draw_boxes(image, boxes, class_names, scores, max_boxes=40, min_score=0.05):\n",
|
||||
" \"\"\"Overlay labeled boxes on an image with formatted scores and label names.\"\"\"\n",
|
||||
" colors = list(ImageColor.colormap.values())\n",
|
||||
" try:\n",
|
||||
" font = ImageFont.truetype(\n",
|
||||
" \"/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf\", 25\n",
|
||||
" )\n",
|
||||
" except OSError:\n",
|
||||
" print(\"Font not found, using default font.\")\n",
|
||||
" font = ImageFont.load_default()\n",
|
||||
"\n",
|
||||
" for i in range(min(len(boxes), max_boxes)):\n",
|
||||
" if scores[i] >= min_score:\n",
|
||||
" ymin, xmin, ymax, xmax = boxes[i]\n",
|
||||
" display_str = \"{}: {}%\".format(class_names[i], int(100 * scores[i]))\n",
|
||||
" color = colors[hash(class_names[i]) % len(colors)]\n",
|
||||
" draw_bounding_box_on_image(\n",
|
||||
" image,\n",
|
||||
" ymin,\n",
|
||||
" xmin,\n",
|
||||
" ymax,\n",
|
||||
" xmax,\n",
|
||||
" color,\n",
|
||||
" font,\n",
|
||||
" display_str_list=[display_str],\n",
|
||||
" )\n",
|
||||
" return image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "nZLVI9TtUuif"
|
||||
},
|
||||
"source": [
|
||||
"## Create a dataset\n",
|
||||
"\n",
|
||||
"This tutorial uses a version of the Salads dataset that is stored in a public Cloud Storage bucket, using a CSV index file.\n",
|
||||
"\n",
|
||||
"Start by doing a quick peek at the data. You count the number of examples by counting the number of rows in the CSV index file (`wc -l`) and then peek at the first few rows."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Pr60Y1fpUuO9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"count = ! gsutil cat $DATASET_FILE | wc -l\n",
|
||||
"print(\"Number of Examples\", int(count[0]))\n",
|
||||
"\n",
|
||||
"print(\"First 10 rows\")\n",
|
||||
"! gsutil cat $DATASET_FILE | head"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "SZEdBfNZUxQn"
|
||||
},
|
||||
"source": [
|
||||
"Next, create the `Dataset` resource using the `create` method for the `ImageDataset` class, which takes the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the `Dataset` resource.\n",
|
||||
"- `gcs_source`: A list of one or more dataset index files to import the data items into the `Dataset` resource.\n",
|
||||
"- `import_schema_uri`: The data labeling schema for the data items.\n",
|
||||
"\n",
|
||||
"This operation may take several minutes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "FlDqM5APU0As"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = aiplatform.ImageDataset.create(\n",
|
||||
" display_name=DATASET_PREFIX + \"_salads\",\n",
|
||||
" gcs_source=[DATASET_FILE],\n",
|
||||
" import_schema_uri=aiplatform.schema.dataset.ioformat.image.bounding_box,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(dataset.resource_name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RB_xY9ipr7ZU"
|
||||
},
|
||||
"source": [
|
||||
"## Train new models\n",
|
||||
"\n",
|
||||
"### Create and run training pipeline\n",
|
||||
"\n",
|
||||
"To train an AutoML model, you perform two steps:\n",
|
||||
"1. Create a training pipeline.\n",
|
||||
"2. Run the pipeline.\n",
|
||||
"\n",
|
||||
"#### Create training pipeline\n",
|
||||
"\n",
|
||||
"An AutoML training pipeline is created with the `AutoMLImageTrainingJob` class, with the following parameters:\n",
|
||||
"\n",
|
||||
"- `display_name`: The human readable name for the `TrainingJob` resource.\n",
|
||||
"- `prediction_type`: The type task to train the model for.\n",
|
||||
" - `classification`: An image classification model.\n",
|
||||
" - `object_detection`: An image object detection model.\n",
|
||||
"- `model_type`: The type of model for deployment. For image object detection, we current support the following:\n",
|
||||
" - `SPINENET`: A model that is available in Vertex Model Garden image object detection training with customizable hyperparameters. Best tailored to be used within Google Cloud, and cannot be exported externally.\n",
|
||||
" - `YOLO`: A model that is available in Vertex Model Garden image object detection training with customizable hyperparameters. Best tailored to be used within Google Cloud, and cannot be exported externally.\n",
|
||||
"- `checkpoint_name`: Optional. The field is reserved for Model Garden model training, based on the provided pre-trained model checkpoint.\n",
|
||||
"- `trainer_config`: Optional. The field is usually used together with the Model Garden model training, when passing the customized configs for the trainer. `anchor_size` cannot be used with `YOLO`.\n",
|
||||
"\n",
|
||||
" Example with all supported parameters:\n",
|
||||
"```\n",
|
||||
" trainer_config = {\n",
|
||||
" 'global_batch_size': '8',\n",
|
||||
" 'learning_rate': '0.001',\n",
|
||||
" 'optimizer_type': 'sgd',\n",
|
||||
" 'optimizer_momentum': '0.9',\n",
|
||||
" 'train_steps': '10000',\n",
|
||||
" 'accelerator_count': '1',\n",
|
||||
" 'anchor_size': '8',\n",
|
||||
" }\n",
|
||||
"```\n",
|
||||
"- `metric_spec`: Dictionary representing metrics to optimize. The dictionary key is the metric_id, which is reported by your training job, with possible values being ('loss', 'AP50') and the dictionary value is the optimization goal of the metric('minimize' or 'maximize').\n",
|
||||
"For example: `metric_spec = {'loss': 'minimize', 'AP50': 'maximize'}`\n",
|
||||
"- `parameter_spec`:Dictionary representing parameters to optimize. The dictionary key is the `metric_id`, which is passed into your training job as a command line key word argument, and the dictionary value is the parameter\n",
|
||||
"specification of the metric. Supported parameter specifications can be found in aiplatform.hyperparameter_tuning.\n",
|
||||
"```\n",
|
||||
" from google.cloud.aiplatform.aiplatform import hpt as hpt\n",
|
||||
"\n",
|
||||
" parameter_spec = {\n",
|
||||
" 'learning_rate': hpt.DoubleParameterSpec(min=1e-7, max=1, scale='linear'), \\\n",
|
||||
" }\n",
|
||||
"```\n",
|
||||
"- `search_algorithm`: The search algorithm specified for the Study. Accepts one of the following:\n",
|
||||
" - `None`: If you do not specify an algorithm, your job uses the default\n",
|
||||
" Vertex AI algorithm. The default algorithm applies Bayesian optimization\n",
|
||||
" to arrive at the optimal solution with a more effective search over the\n",
|
||||
" parameter space.\n",
|
||||
" - `grid`: A simple grid search within the feasible space. This option is\n",
|
||||
" particularly useful if you want to specify a quantity of trials that is greater than the number of points in the feasible space. In such cases, if you do not specify a grid search, the Vertex AI default algorithm may generate duplicate suggestions. To use grid search, all parameter specs must be of type `IntegerParameterSpec`, `CategoricalParameterSpec`, or `DiscreteParameterSpec`.\n",
|
||||
" - `random`: A simple random search within the feasible space.\n",
|
||||
"- `measurement_selection`: This indicates which measurement to use\n",
|
||||
"if/when the service automatically selects the final measurement from\n",
|
||||
"previously reported intermediate measurements.\n",
|
||||
" Accepts: `best`, `last` Choose this based on two considerations:\n",
|
||||
" - A): Do you expect your measurements to monotonically improve? If so,\n",
|
||||
" choose `last`. On the other hand, if you\\'re in a situation where\n",
|
||||
" your system can **over-train** and you expect the performance to get\n",
|
||||
" better for a while but then start declining, choose `best`.\n",
|
||||
" - B): Are your measurements significantly noisy and/or irreproducible? If\n",
|
||||
" so, `best` will tend to be over-optimistic, and it may be better\n",
|
||||
" to choose `last`. If both or neither of (A) and (B) apply, it\n",
|
||||
" doesn't matter which selection type is chosen.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
|
||||
"\n",
|
||||
"TRAINER_CONFIG = {\n",
|
||||
" \"global_batch_size\": \"8\",\n",
|
||||
" \"learning_rate\": \"0.001\",\n",
|
||||
" \"train_steps\": \"10000\",\n",
|
||||
" \"accelerator_count\": \"1\",\n",
|
||||
"}\n",
|
||||
"METRIC_SPEC_KEY = \"AP50\"\n",
|
||||
"METRIC_SPEC_VALUE = \"maximize\"\n",
|
||||
"SEARCH_ALGORITHM = \"random\"\n",
|
||||
"MEASUREMENT_SELECTION = \"best\"\n",
|
||||
"MODEL_TYPE = \"SPINENET\" # @param {type:\"string\"} one of the values [\"SPINENET\", \"YOLO\"]\n",
|
||||
"\n",
|
||||
"PARAMETER_SPEC = {}\n",
|
||||
"if MODEL_TYPE == \"YOLO\":\n",
|
||||
" PARAMETER_SPEC = {\n",
|
||||
" \"learning_rate\": hpt.DiscreteParameterSpec(\n",
|
||||
" values=[0.001, 0.1],\n",
|
||||
" scale=\"linear\",\n",
|
||||
" ),\n",
|
||||
" \"weight_decay\": hpt.DiscreteParameterSpec(\n",
|
||||
" values=[0.0001, 0.001],\n",
|
||||
" scale=\"linear\",\n",
|
||||
" ),\n",
|
||||
" }\n",
|
||||
"else:\n",
|
||||
" PARAMETER_SPEC = {\n",
|
||||
" \"learning_rate\": hpt.DiscreteParameterSpec(\n",
|
||||
" values=[0.001, 0.01], scale=\"linear\"\n",
|
||||
" ),\n",
|
||||
" \"anchor_size\": hpt.DiscreteParameterSpec(values=[2, 4], scale=\"reverse_log\"),\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
"job = aiplatform.AutoMLImageTrainingJob(\n",
|
||||
" display_name=get_job_name_with_datetime(TRAINING_JOB_PREFIX),\n",
|
||||
" prediction_type=\"object_detection\",\n",
|
||||
" model_type=MODEL_TYPE,\n",
|
||||
" base_model=None,\n",
|
||||
" trainer_config=TRAINER_CONFIG,\n",
|
||||
" metric_spec={METRIC_SPEC_KEY: METRIC_SPEC_VALUE},\n",
|
||||
" parameter_spec=PARAMETER_SPEC,\n",
|
||||
" search_algorithm=SEARCH_ALGORITHM,\n",
|
||||
" measurement_selection=MEASUREMENT_SELECTION,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(job)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"#### Run the training pipeline\n",
|
||||
"\n",
|
||||
"Next, run the DAG to start the training job by invoking the method `run`, with the following parameters:\n",
|
||||
"\n",
|
||||
"- `dataset`: The `Dataset` resource to train the model.\n",
|
||||
"- `model_display_name`: The human readable name for the trained model.\n",
|
||||
"- `training_fraction_split`: The percentage of the dataset to use for training.\n",
|
||||
"- `test_fraction_split`: The percentage of the dataset to use for test (holdout data).\n",
|
||||
"- `validation_fraction_split`: The percentage of the dataset to use for validation.\n",
|
||||
"- `budget_milli_node_hours`: (optional) Maximum training time specified in unit of millihours (1000 = hour).\n",
|
||||
"- `disable_early_stopping`: If `True`, training may be completed before using the entire budget if the service believes it cannot further improve on the model objective measurements.\n",
|
||||
"\n",
|
||||
"The `run` method when completed returns the `Model` resource.\n",
|
||||
"\n",
|
||||
"The execution of the training pipeline will take up to 60 minutes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = job.run(\n",
|
||||
" dataset=dataset,\n",
|
||||
" model_display_name=get_job_name_with_datetime(\"salads\"),\n",
|
||||
" training_fraction_split=0.8,\n",
|
||||
" validation_fraction_split=0.1,\n",
|
||||
" test_fraction_split=0.1,\n",
|
||||
" budget_milli_node_hours=20000,\n",
|
||||
" disable_early_stopping=False,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Model is: \", model)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"## Test trained models\n",
|
||||
"This section shows how to test the trained models.\n",
|
||||
"1. Deploy models from Model Registry\n",
|
||||
"2. Run online predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "mj723H4NXu4v"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Deploy model from Model Registry\n",
|
||||
"\n",
|
||||
"deploy_model_name = get_job_name_with_datetime(DEPLOY_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"print(\"The deployed job name is: \", deploy_model_name)\n",
|
||||
"\n",
|
||||
"endpoint = model.deploy(\n",
|
||||
" deployed_model_display_name=deploy_model_name,\n",
|
||||
" machine_type=\"\",\n",
|
||||
" traffic_split={\"0\": 100},\n",
|
||||
" accelerator_type=None,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" min_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"print(\"endpoint id is: \", endpoint_id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NTYvgFv6XyEe"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Run online predictions\n",
|
||||
"\n",
|
||||
"# test image file path from a GCS bucket\n",
|
||||
"test_filepath = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"with tf.io.gfile.GFile(test_filepath, \"rb\") as f:\n",
|
||||
" content = f.read()\n",
|
||||
"\n",
|
||||
"# The format of each instance should conform to the deployed model's prediction input schema.\n",
|
||||
"instances = [{\"content\": base64.b64encode(content).decode(\"utf-8\")}]\n",
|
||||
"\n",
|
||||
"prediction = endpoint.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"img = load_img(test_filepath)\n",
|
||||
"display_image(img)\n",
|
||||
"print(prediction)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "frcGP5HFX1XN"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "H2m8-u0IX4dX"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.undeploy_all()\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_proprietary_image_object_detection.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Bart Large CNN\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_bart_large_cnn.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_bart_large_cnn.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_bart_large_cnn.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [Bart Large CNN](https://huggingface.co/facebook/bart-large-cnn) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip3 install --upgrade google-cloud-aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b60a4d7100bf"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
"google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image.\n",
|
||||
"# The model artifacts are embedded within the container, except for model weights which will be downloaded during deployment.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" \"\"\"Uploads and deploys the model to Vertex AI endpoint for prediction.\"\"\"\n",
|
||||
" model_name = \"bart_large_cnn\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(model_id=\"facebook/bart-large-cnn\", task=\"summarization\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "nTs3JBT3W_lJ"
|
||||
},
|
||||
"source": [
|
||||
"## Send prediction request"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ARTICLE = \"\"\" New York (CNN)When Liana Barrientos was 23 years old, she got married in Westchester County, New York.\n",
|
||||
"A year later, she got married again in Westchester County, but to a different man and without divorcing her first husband.\n",
|
||||
"Only 18 days after that marriage, she got hitched yet again. Then, Barrientos declared \"I do\" five more times, sometimes only within two weeks of each other.\n",
|
||||
"In 2010, she married once more, this time in the Bronx. In an application for a marriage license, she stated it was her \"first and only\" marriage.\n",
|
||||
"Barrientos, now 39, is facing two criminal counts of \"offering a false instrument for filing in the first degree,\" referring to her false statements on the\n",
|
||||
"2010 marriage license application, according to court documents.\n",
|
||||
"Prosecutors said the marriages were part of an immigration scam.\n",
|
||||
"On Friday, she pleaded not guilty at State Supreme Court in the Bronx, according to her attorney, Christopher Wright, who declined to comment further.\n",
|
||||
"After leaving court, Barrientos was arrested and charged with theft of service and criminal trespass for allegedly sneaking into the New York subway through an emergency exit, said Detective\n",
|
||||
"Annette Markowski, a police spokeswoman. In total, Barrientos has been married 10 times, with nine of her marriages occurring between 1999 and 2002.\n",
|
||||
"All occurred either in Westchester County, Long Island, New Jersey or the Bronx. She is believed to still be married to four men, and at one time, she was married to eight men at once, prosecutors say.\n",
|
||||
"Prosecutors said the immigration scam involved some of her husbands, who filed for permanent residence status shortly after the marriages.\n",
|
||||
"Any divorces happened only after such filings were approved. It was unclear whether any of the men will be prosecuted.\n",
|
||||
"The case was referred to the Bronx District Attorney\\'s Office by Immigration and Customs Enforcement and the Department of Homeland Security\\'s\n",
|
||||
"Investigation Division. Seven of the men are from so-called \"red-flagged\" countries, including Egypt, Turkey, Georgia, Pakistan and Mali.\n",
|
||||
"Her eighth husband, Rashid Rajput, was deported in 2006 to his native Pakistan after an investigation by the Joint Terrorism Task Force.\n",
|
||||
"If convicted, Barrientos faces up to four years in prison. Her next court appearance is scheduled for May 18.\n",
|
||||
"\"\"\"\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"text\": ARTICLE},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "xKj4wBQNWuHT"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_bart_large_cnn.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,465 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - BLIP2\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_blip2.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_blip2.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_blip2.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [BLIP2](https://huggingface.co/Salesforce/blip2-opt-2.7b) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image.\n",
|
||||
"# The model artifacts are embedded within the container, except for model weights which will be downloaded during deployment.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"blip2\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send images to get descriptions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "19e8aeec969c"
|
||||
},
|
||||
"source": [
|
||||
"### Image captioning"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"Salesforce/blip2-opt-2.7b\", task=\"image-to-text\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 10 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image)},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "01e11d85d504"
|
||||
},
|
||||
"source": [
|
||||
"### VQA (Visual-Question-Answering)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "26018d961cf9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"Salesforce/blip2-opt-2.7b\", task=\"visual-question-answering\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 10 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "f19c342829fd"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\n",
|
||||
" \"https://media.newyorker.com/cartoons/63dc6847be24a6a76d90eb99/master/w_1160,c_limit/230213_a26611_838.jpg\"\n",
|
||||
")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"question = \"Question: What are they doing? Answer:\"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image), \"text\": question},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(question)\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "712eb9d0b336"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_blip2.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - BLIP Image Captioning\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_blip_image_captioning.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_blip_image_captioning.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_blip_image_captioning.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [BLIP Image Captioning](https://huggingface.co/Salesforce/blip-image-captioning-base) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"blip-image-captioning\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send images to get descriptions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"Salesforce/blip-image-captioning-base\", task=\"image-to-text\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image)},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_blip_image_captioning.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - BLIP VQA\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_blip_vqa.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_blip_vqa.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_blip_vqa.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [BLIP VQA](https://huggingface.co/Salesforce/blip-vqa-base) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"blip-vqa\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send images and questions to get answers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"Salesforce/blip-vqa-base\", task=\"visual-question-answering\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"question = \"Which cat is bigger?\"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image), \"text\": question},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(question)\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_blip_vqa.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - CLIP\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_clip.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_clip.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_clip.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [CLIP](https://huggingface.co/openai/clip-vit-base-patch32) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"clip\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99f3c6b404b5"
|
||||
},
|
||||
"source": [
|
||||
"### Zero-shot image classification"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"openai/clip-vit-base-patch32\", task=\"zero-shot-image-classification\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image1 = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"image2 = download_image(\"http://images.cocodataset.org/val2017/000000000285.jpg\")\n",
|
||||
"grid = image_grid([image1, image2], 1, 2)\n",
|
||||
"display(grid)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image1), \"text\": \"two cats\"},\n",
|
||||
" {\"image\": image_to_base64(image2), \"text\": \"a bear\"},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ddf9e9ec7b58"
|
||||
},
|
||||
"source": [
|
||||
"### Image/text feature embedding"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "8897bbac6887"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"openai/clip-vit-base-patch32\", task=\"feature-embedding\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9f854c45f8f3"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed after the above model deployment step succeeds and before you run the next step below. Otherwise you might see a ServiceUnavailable: 503 502:Bad Gateway error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2abd54335f36"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"# Extract feature embedding of images.\n",
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"display(image)\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image)},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"image_features = np.array(preds[0][\"image_features\"])\n",
|
||||
"print(image_features.shape)\n",
|
||||
"\n",
|
||||
"# Extract feature embedding of texts.\n",
|
||||
"instances = [\n",
|
||||
" {\"text\": \"two cats\"},\n",
|
||||
" {\"text\": \"hello world\"},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"text_features = np.array(preds[0][\"text_features\"])\n",
|
||||
"print(text_features.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "712eb9d0b336"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_clip.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,660 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - ControlNet\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_controlnet.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_controlnet.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_controlnet.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates finetuning the [ControlNet](https://huggingface.co/lllyasviel/ControlNet) with the [fusing/fill50k](https://huggingface.co/datasets/fusing/fill50k) dataset and deploying the model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Finetune the ControlNet model.\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for text-guided-image-to-image.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip3 install --upgrade google-cloud-aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b60a4d7100bf"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
"google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fb671e75ca7b"
|
||||
},
|
||||
"source": [
|
||||
"### Install dependencies"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dc8ee367fb42"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Install gdown for downloading example training images.\n",
|
||||
"!pip install gdown\n",
|
||||
"# Install libs for generating conditioning images for ControlNet.\n",
|
||||
"!pip install opencv-python"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5244aac3d929"
|
||||
},
|
||||
"source": [
|
||||
"Restart the notebook kernel after installs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "567212ff53a6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import IPython\n",
|
||||
"\n",
|
||||
"app = IPython.Application.instance()\n",
|
||||
"app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The service account you created in step-5 above, it's like \"<account_name>@<project>.iam.gserviceaccount.com\"\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training docker image. It contains training scripts and models.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-train:latest\"\n",
|
||||
"\n",
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import cv2\n",
|
||||
"import numpy as np\n",
|
||||
"import requests\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def canny(image):\n",
|
||||
" image = np.array(image)\n",
|
||||
" image = cv2.Canny(image, 100, 200)\n",
|
||||
" image = image[:, :, None]\n",
|
||||
" image = np.concatenate([image, image, image], axis=2)\n",
|
||||
" image = Image.fromarray(image)\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"controlnet\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune with fill50k dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0dc65d8f0689"
|
||||
},
|
||||
"source": [
|
||||
"This section uses the [fusing/fill50k](https://huggingface.co/datasets/fusing/fill50k) dataset to finetune the ControlNet model.\n",
|
||||
"\n",
|
||||
"The job will run on 1 A100 GPU and take ~7 hours to finish 1 epoch of training.\n",
|
||||
"\n",
|
||||
"The ControlNet model will be saved after the finetuning job finishs and it can be loaded to run inference later."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65467b361315"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-trained stable diffusion model to be loaded.\n",
|
||||
"stable_diffusion_model_id = \"runwayml/stable-diffusion-v1-5\"\n",
|
||||
"# The datase id to be loaded.\n",
|
||||
"dataset_id = \"fusing/fill50k\"\n",
|
||||
"# The output path.\n",
|
||||
"output_dir = f\"/gcs/{GCS_BUCKET}/controlnet/output\"\n",
|
||||
"# The training steps. If provided, it overrides num_train_epochs. Set it to None to use num_train_epochs only.\n",
|
||||
"max_train_steps = 10 # @param {type:\"integer\"}\n",
|
||||
"# The training epochs. Set it to a bigger number, like 10, to make training converge better.\n",
|
||||
"num_train_epochs = 1 # @param {type:\"integer\"}\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"num_nodes = 1\n",
|
||||
"gpu_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"num_gpus = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = create_job_name(\"controlnet\")\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"# See https://github.com/huggingface/diffusers/blob/main/examples/controlnet/train_controlnet.py\n",
|
||||
"# for a full list of training arguments.\n",
|
||||
"model = job.run(\n",
|
||||
" args=[\n",
|
||||
" f\"--num_machines={num_nodes}\",\n",
|
||||
" f\"--num_processes={num_gpus}\",\n",
|
||||
" \"--machine_rank=0\",\n",
|
||||
" \"--mixed_precision=no\",\n",
|
||||
" \"--gpu_ids=all\",\n",
|
||||
" \"--same_network\",\n",
|
||||
" \"--dynamo_backend=no\",\n",
|
||||
" \"controlnet/train_controlnet.py\",\n",
|
||||
" \"--tracker_project_name=train_controlnet\",\n",
|
||||
" f\"--pretrained_model_name_or_path={stable_diffusion_model_id}\",\n",
|
||||
" f\"--output_dir={output_dir}\",\n",
|
||||
" f\"--dataset_name={dataset_id}\",\n",
|
||||
" f\"--max_train_steps={max_train_steps}\",\n",
|
||||
" f\"--num_train_epochs={num_train_epochs}\",\n",
|
||||
" \"--resolution=512\",\n",
|
||||
" \"--learning_rate=1e-5\",\n",
|
||||
" \"--train_batch_size=2\",\n",
|
||||
" \"--checkpointing_steps=50000\",\n",
|
||||
" \"--checkpoints_total_limit=1\",\n",
|
||||
" ],\n",
|
||||
" replica_count=num_nodes,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=gpu_type,\n",
|
||||
" accelerator_count=num_gpus,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bf7f82732e61"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and Deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1cc26e68d7b0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cd7b56421392"
|
||||
},
|
||||
"source": [
|
||||
"### Pre-trained canny model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6d331b1ea337"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the pre-trained [lllyasviel/sd-controlnet-canny](https://huggingface.co/lllyasviel/sd-controlnet-canny) model for the text-guided image-to-image task. When deployed on one V100 GPU, the average inference time of a request is ~15 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bf55e38815dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"lllyasviel/sd-controlnet-canny\", task=\"controlnet\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4ab04da3ec9a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"init_image = download_image(\n",
|
||||
" \"https://huggingface.co/takuma104/controlnet_dev/resolve/main/gen_compare/output_images/diffusers/output_bird_canny_1.png\"\n",
|
||||
")\n",
|
||||
"display(init_image)\n",
|
||||
"image = canny(init_image)\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"bird\",\n",
|
||||
" \"image\": image_to_base64(image),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "af21a3cff1e0"
|
||||
},
|
||||
"source": [
|
||||
"Clean up resources:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c1e51f764a60"
|
||||
},
|
||||
"source": [
|
||||
"### Custom finetuned fill50k model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fa686a54047c"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the finetuned fill50k model above for the text-guided image-to-image task. When deployed on one V100 GPU, the averaged inference time of a request is ~15 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65e32356fbd1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=f\"gs://{GCS_BUCKET}/controlnet/output\", task=\"controlnet\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "83a50fd4a1ed"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"init_image = download_image(\n",
|
||||
" \"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/controlnet_training/conditioning_image_1.png\"\n",
|
||||
")\n",
|
||||
"display(init_image)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"red circle with green background\",\n",
|
||||
" \"image\": image_to_base64(init_image, format=\"PNG\"),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ed3795d474b9"
|
||||
},
|
||||
"source": [
|
||||
"Clean up resources:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b53b883257b4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_controlnet.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,362 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Dolly V2\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_dolly_v2.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_dolly_v2.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_dolly_v2.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [Dolly-V2-7b](https://huggingface.co/databricks/dolly-v2-7b) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip3 install --upgrade google-cloud-aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b60a4d7100bf"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
"google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image.\n",
|
||||
"# The model artifacts are embedded within the container, except for model weights which will be downloaded during deployment.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-dolly-v2-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def deploy_model(model_id):\n",
|
||||
" \"\"\"Uploads and deploys the model to Vertex AI endpoint for prediction.\"\"\"\n",
|
||||
" model_name = \"dolly_v2_7b\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"a2-highgpu-1g\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_A100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 A100 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(model_id=\"databricks/dolly-v2-7b\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 10 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ohPQGAawXtAf"
|
||||
},
|
||||
"source": [
|
||||
"## Send prediction request"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"input_text = \"Explain to me the difference between nuclear fission and fusion.\"\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"text\": input_text},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "uXLWt43CXMgj"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_dolly_v2.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,655 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Falcon Instruct (PEFT)\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_peft.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_peft.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_falcon_instruct_peft.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> (A Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying prebuilt Falcon Instruct models, and also finetuning and deploying Falcon Instruct models with performance efficient finetuning libraries ([PEFT](https://github.com/huggingface/peft)) in Vertex AI.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Deploy prebuilt Falcon Instruct models\n",
|
||||
"- Finetune and deploy Falcon Instruct models with PEFT, supporting\n",
|
||||
"\n",
|
||||
"| Models | LoRA |\n",
|
||||
"| :- | :- |\n",
|
||||
"| [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct) | Y |\n",
|
||||
"| [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) | Y |\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output.\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
|
||||
"DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
|
||||
"MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
|
||||
"\n",
|
||||
"# The service account looks like:\n",
|
||||
"# '@.iam.gserviceaccount.com'\n",
|
||||
"# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
|
||||
"# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
|
||||
"# The service account for deploying fine tuned model.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training and serving docker images.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-peft-train\"\n",
|
||||
"PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-peft-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" \"\"\"Gets the job name with date time when triggering training or deployment\n",
|
||||
" jobs in Vertex AI.\n",
|
||||
" \"\"\"\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(\n",
|
||||
" model_name,\n",
|
||||
" base_model_id,\n",
|
||||
" finetuned_lora_model_path,\n",
|
||||
" service_account,\n",
|
||||
" task,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
"):\n",
|
||||
" \"\"\"Deploys trained models into Vertex AI.\"\"\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"BASE_MODEL_ID\": base_model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" if finetuned_lora_model_path:\n",
|
||||
" serving_env[\"FINETUNED_LORA_MODEL_PATH\"] = finetuned_lora_model_path\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/peft_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=service_account,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8neJc8CnDDpu"
|
||||
},
|
||||
"source": [
|
||||
"## Deploy prebuilt Falcon Instruct models\n",
|
||||
"\n",
|
||||
"This section deploys prebuilt Falcon Instruct models on the Endpoint. The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) are ~15.5G and ~38.2G separately with the default settings. We use V100 in deployments as an example. Please use A100 (40G) or A100 (80G) to get better inferences."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2MjaORIIFDVu"
|
||||
},
|
||||
"source": [
|
||||
"Set the prebuilt model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "E8OiHHNNE_wj"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prebuilt_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dHFW7yvjaVFV"
|
||||
},
|
||||
"source": [
|
||||
"We use the PEFT serving images to deploy prebuilt Falcon Instruct models, by setting finetuning LoRA model paths as empty."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Uak1pyEeExYM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Sets V100 to deploy tiiuae/falcon-7b-instruct as an example.\n",
|
||||
"machine_type = \"n1-standard-8\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"\n",
|
||||
"# Sets A100 (40G) to deploy tiiuae/falcon-7b-instruct or tiiuae/falcon-40b-instruct.\n",
|
||||
"# machine_type = \"a2-highgpu-1g\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"\n",
|
||||
"# Sets A100 (80G) to deploy falcon-40b-instruct models for faster inferences.\n",
|
||||
"# machine_type = \"a2-ultragpu-1g\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"\n",
|
||||
"model_without_peft, endpoint_without_peft = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"falcon-instruct-serve\"),\n",
|
||||
" base_model_id=prebuilt_model_id,\n",
|
||||
" finetuned_lora_model_path=\"\", # This will avoid override finetuning models.\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"instruct-lora\",\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name:\", endpoint_without_peft.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "sGKIjgmDFRW2"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The prebuilt model weights will be downloaded on the fly from the orginal location after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
|
||||
"\n",
|
||||
"Example:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"Human: What is a car?\n",
|
||||
"Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "rDHsCOqvFYBi"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint_without_peft.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint_without_peft = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"What is a car?\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint_without_peft.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"for prediction in response.predictions[0]:\n",
|
||||
" print(prediction[\"generated_text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune and deploy Falcon Instruct models with PEFT\n",
|
||||
"\n",
|
||||
"This section demonstrates how to finetune and dpeloy Falcon Instruct models with PEFT LoRA."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5qCrm_kJH5cz"
|
||||
},
|
||||
"source": [
|
||||
"Set the base model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "N3UBLiYrM3sU"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "iWGwJHqI7LMs"
|
||||
},
|
||||
"source": [
|
||||
"### Finetune"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KKEYoRfiHDVv"
|
||||
},
|
||||
"source": [
|
||||
"Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
|
||||
"\n",
|
||||
"This example uses the dataset [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco).\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages are ~11G and ~34G for finetuning LoRA models for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) separately with default training parameters and the example dataset. Falcon-7b-instruct can be finetuned on 1 P100/V100, and falcon-40b-instruct can be finetuned on 1 A100 (40G)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65467b361315"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"# Uses V100 to finetune falcon-70b-instruct.\n",
|
||||
"machine_type = \"n1-standard-8\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"# Uses A100 to finetune falcon-40b-instruct.\n",
|
||||
"# machine_type = \"a2-highgpu-1g\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"replica_count = 1\n",
|
||||
"accelerator_count = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = get_job_name_with_datetime(\"falcon-instruct-lora-train\")\n",
|
||||
"train_job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
|
||||
"output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"max_steps = 10\n",
|
||||
"train_job.run(\n",
|
||||
" args=[\n",
|
||||
" \"--task=instruct-lora\",\n",
|
||||
" f\"--pretrained_model_id={base_model_id}\",\n",
|
||||
" f\"--dataset_name={dataset_name}\",\n",
|
||||
" f\"--output_dir={output_dir_gcsfuse}\",\n",
|
||||
" \"--lora_rank=64\",\n",
|
||||
" \"--lora_alpha=16\",\n",
|
||||
" \"--lora_dropout=0.1\",\n",
|
||||
" \"--warmup_ratio=0.03\",\n",
|
||||
" f\"--max_steps={max_steps}\",\n",
|
||||
" \"--max_seq_length=512\",\n",
|
||||
" \"--learning_rate=2e-4\",\n",
|
||||
" ],\n",
|
||||
" replica_count=replica_count,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=accelerator_count,\n",
|
||||
" boot_disk_size_gb=500,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Trained models were saved in: \", output_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jqmCtkGnhDmp"
|
||||
},
|
||||
"source": [
|
||||
"### Deploy\n",
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) with LoRA weights are ~15.5G and ~38.2G separately with the default settings. We use V100 in deployments as an example. Please use A100 (40G) or A100 (80G) to get better inferences."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bf55e38815dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Sets V100 to deploy tiiuae/falcon-7b-instruct as an example.\n",
|
||||
"machine_type = \"n1-standard-8\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"\n",
|
||||
"# Sets A100 (40G) to deploy tiiuae/falcon-7b-instruct or tiiuae/falcon-40b-instruct.\n",
|
||||
"# machine_type = \"a2-highgpu-1g\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"\n",
|
||||
"# Sets A100 (80G) to deploy falcon-40b-instruct models for faster inferences.\n",
|
||||
"# machine_type = \"a2-ultragpu-1g\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"\n",
|
||||
"model_with_peft, endpoint_with_peft = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"falcon-instruct-peft-serve\"),\n",
|
||||
" base_model_id=base_model_id,\n",
|
||||
" finetuned_lora_model_path=os.path.join(output_dir, \"checkpoint-\" + str(max_steps)),\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"instruct-lora\",\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name:\", endpoint_with_peft.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: After the deployment succeeds, the base model weights will be downloaded one the fly from the original location and LoRA model weights will be downloaded from the GCS bucket used in training above. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
|
||||
"\n",
|
||||
"Example:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"Human: What is a car?\n",
|
||||
"Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4ab04da3ec9a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint_with_peft.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint_with_peft = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"What is a car?\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint_with_peft.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"for prediction in response.predictions[0]:\n",
|
||||
" print(prediction[\"generated_text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "af21a3cff1e0"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete custom train jobs.\n",
|
||||
"train_job.delete()\n",
|
||||
"\n",
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint_without_peft.delete(force=True)\n",
|
||||
"endpoint_with_peft.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model_without_peft.delete()\n",
|
||||
"model_with_peft.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_falcon_instruct_peft.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,421 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - InstructPix2Pix\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_instructpix2pix.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_instructpix2pix.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_instructpix2pix.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [InstructPix2Pix](https://huggingface.co/timbrooks/instruct-pix2pix) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for text-guided image-to-image.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"instruct-pix2pix\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c277da31bde6"
|
||||
},
|
||||
"source": [
|
||||
"### Text-guided image-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "a5a86996222c"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the InstructPix2Pix model for the text-guided image-to-image task.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send prompts to the endpoint to generated images.\n",
|
||||
"\n",
|
||||
"When deployed on one V100 GPU, the averaged inference time of a request is ~15 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"timbrooks/instruct-pix2pix\", task=\"instruct-pix2pix\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"init_image = download_image(\n",
|
||||
" \"https://huggingface.co/datasets/diffusers/diffusers-images-docs/resolve/main/mountain.png\"\n",
|
||||
")\n",
|
||||
"display(init_image)\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"Add fire to the mountain\",\n",
|
||||
" \"image\": image_to_base64(init_image),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_instructpix2pix.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - LayoutML Document QA\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_layoutml_document_qa.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_layoutml_document_qa.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_layoutml_document_qa.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [LayoutML](https://huggingface.co/impira/layoutlm-document-qa) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"layoutml\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send document images and questions to get answers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"impira/layoutlm-document-qa\", task=\"document-question-answering\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\n",
|
||||
" \"https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png\"\n",
|
||||
")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"question = \"What is the name of the signer?\"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image), \"text\": \"\"},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(question)\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_layoutml_document_qa.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,610 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - OpenLLaMA (PEFT)\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_openllama_peft.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> (A Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying prebuilt OpenLLaMA, and also finetuning and deploying OpenLLaMA with performance efficient finetuning libraries ([PEFT](https://github.com/huggingface/peft)) in Vertex AI.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Deploy prebuilt OpenLLaMA\n",
|
||||
"- Finetune and deploy OpenLLaMA with PEFT, supporting\n",
|
||||
"\n",
|
||||
"| Models | LoRA |\n",
|
||||
"| :- | :- |\n",
|
||||
"| [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b) | Y |\n",
|
||||
"| [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b) | Y |\n",
|
||||
"| [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) | Y |\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output.\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
|
||||
"DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
|
||||
"MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
|
||||
"\n",
|
||||
"# The service account looks like:\n",
|
||||
"# '@.iam.gserviceaccount.com'\n",
|
||||
"# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
|
||||
"# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
|
||||
"# The service account for deploying fine tuned model.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training and serving docker images.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-peft-train\"\n",
|
||||
"PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-peft-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" \"\"\"Gets the job name with date time when triggering training or deployment\n",
|
||||
" jobs in Vertex AI.\n",
|
||||
" \"\"\"\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(\n",
|
||||
" model_name,\n",
|
||||
" base_model_id,\n",
|
||||
" finetuned_lora_model_path,\n",
|
||||
" service_account,\n",
|
||||
" task,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
"):\n",
|
||||
" \"\"\"Deploys trained models into Vertex AI.\"\"\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"BASE_MODEL_ID\": base_model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" if finetuned_lora_model_path:\n",
|
||||
" serving_env[\"FINETUNED_LORA_MODEL_PATH\"] = finetuned_lora_model_path\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/peft_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=service_account,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8neJc8CnDDpu"
|
||||
},
|
||||
"source": [
|
||||
"## Deploy Prebuilt OpenLLaMA\n",
|
||||
"\n",
|
||||
"This section deploys prebuilt OpenLLaMA models on the Endpoint. The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) are ~5.3G, ~8.7G and ~15.2G separately with the default settings. We use V100 in deployments for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2MjaORIIFDVu"
|
||||
},
|
||||
"source": [
|
||||
"Set the prebuilt model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "E8OiHHNNE_wj"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"prebuilt_model_id = \"openlm-research/open_llama_3b\" # @param [\"openlm-research/open_llama_3b\", \"openlm-research/open_llama_7b\", \"openlm-research/open_llama_13b\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dHFW7yvjaVFV"
|
||||
},
|
||||
"source": [
|
||||
"We use the PEFT serving images to deploy prebuilt OpenLLaMA models, by setting finetuning LoRA model paths as empty."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Uak1pyEeExYM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_without_peft, endpoint_without_peft = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"openllama-serve\"),\n",
|
||||
" base_model_id=prebuilt_model_id,\n",
|
||||
" finetuned_lora_model_path=\"\", # This will avoid override finetuning models.\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"causal-language-modeling-lora\",\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name:\", endpoint_without_peft.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "sGKIjgmDFRW2"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The prebuilt model weights will be downloaded on the fly from the orginal location after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "rDHsCOqvFYBi"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint_without_peft.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint_without_peft = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"Hi, Google.\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint_without_peft.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"for prediction in response.predictions[0]:\n",
|
||||
" print(prediction[\"generated_text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune and deploy OpenLLaMA with PEFT\n",
|
||||
"\n",
|
||||
"This section demonstrates how to finetune and dpeloy OpenLLaMA with PEFT LoRA."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5qCrm_kJH5cz"
|
||||
},
|
||||
"source": [
|
||||
"Set the base model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "N3UBLiYrM3sU"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_model_id = \"openlm-research/open_llama_3b\" # @param [\"openlm-research/open_llama_3b\", \"openlm-research/open_llama_7b\", \"openlm-research/open_llama_13b\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "iWGwJHqI7LMs"
|
||||
},
|
||||
"source": [
|
||||
"### Finetune"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KKEYoRfiHDVv"
|
||||
},
|
||||
"source": [
|
||||
"Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
|
||||
"\n",
|
||||
"This example uses the dataset [Abirate/english_quotes](https://huggingface.co/datasets/Abirate/english_quotes).\n",
|
||||
"\n",
|
||||
"In order to make the finetuning efficiently, we enabled quantization (8bits) when loading pretrained models for finetuning LoRA models. The peak GPU memory usages are ~7G, ~10G and ~16G for finetuning LoRA models for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) separately with default training parameters and the example dataset. In theory, open_llama_3b and open_llama_7b can be finetuned on 1 V100, and open_llama_13b can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65467b361315"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset_name = \"Abirate/english_quotes\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"# machine_type = \"n1-standard-8\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"replica_count = 1\n",
|
||||
"accelerator_count = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = get_job_name_with_datetime(\"openllama-lora-train\")\n",
|
||||
"train_job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
|
||||
"output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"train_job.run(\n",
|
||||
" args=[\n",
|
||||
" \"--task=causal-language-modeling-lora\",\n",
|
||||
" f\"--pretrained_model_id={base_model_id}\",\n",
|
||||
" f\"--dataset_name={dataset_name}\",\n",
|
||||
" f\"--output_dir={output_dir_gcsfuse}\",\n",
|
||||
" \"--lora_rank=16\",\n",
|
||||
" \"--lora_alpha=32\",\n",
|
||||
" \"--lora_dropout=0.05\",\n",
|
||||
" \"--warmup_steps=10\",\n",
|
||||
" \"--max_steps=10\",\n",
|
||||
" \"--learning_rate=2e-4\",\n",
|
||||
" ],\n",
|
||||
" replica_count=replica_count,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=accelerator_count,\n",
|
||||
" boot_disk_size_gb=500,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Trained models were saved in: \", output_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jqmCtkGnhDmp"
|
||||
},
|
||||
"source": [
|
||||
"### Deploy\n",
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) with LoRA weights are ~5.3G, ~8.7G and ~15.2G separately with the default settings. We use V100 in deployments for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bf55e38815dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model_with_peft, endpoint_with_peft = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"openllama-peft-serve\"),\n",
|
||||
" base_model_id=base_model_id,\n",
|
||||
" finetuned_lora_model_path=output_dir,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"causal-language-modeling-lora\",\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name:\", endpoint_with_peft.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: After the deployment succeeds, the base model weights will be downloaded one the fly from the original location and LoRA model weights will be downloaded from the GCS bucket used in training above. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4ab04da3ec9a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint_with_peft.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint_with_peft = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"Hi, Google.\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint_with_peft.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"for prediction in response.predictions[0]:\n",
|
||||
" print(prediction[\"generated_text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "af21a3cff1e0"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete custom train jobs.\n",
|
||||
"train_job.delete()\n",
|
||||
"\n",
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint_without_peft.delete(force=True)\n",
|
||||
"endpoint_with_peft.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model_without_peft.delete()\n",
|
||||
"model_with_peft.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_openllama_peft.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,415 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - OWL-ViT\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_owlvit.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_owlvit.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_owlvit.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [OWL-ViT](https://huggingface.co/google/owlvit-base-patch32) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import matplotlib.patches as patches\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def draw_image_with_boxes(image, boxes):\n",
|
||||
" fig, ax = plt.subplots()\n",
|
||||
" plt.axis(\"off\")\n",
|
||||
" ax.imshow(image)\n",
|
||||
" if len(boxes) == 0:\n",
|
||||
" return\n",
|
||||
" boxes = boxes[\"boxes\"]\n",
|
||||
" for box in boxes:\n",
|
||||
" x, y = box[\"xmin\"], box[\"ymin\"]\n",
|
||||
" width, height = box[\"xmax\"] - x, box[\"ymax\"] - y\n",
|
||||
" rect = patches.Rectangle(\n",
|
||||
" (x, y), width, height, linewidth=2, edgecolor=\"yellow\", facecolor=\"none\"\n",
|
||||
" )\n",
|
||||
" ax.add_patch(rect)\n",
|
||||
" plt.show()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"owl-vit\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send images and object texts to get bounding boxes."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"google/owlvit-base-patch32\", task=\"zero-shot-object-detection\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image), \"text\": \"cat\"},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"draw_image_with_boxes(image, preds[0])\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_owlvit.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,953 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - PEFT\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_peft.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_peft.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_peft.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> (A Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates finetuning models with performance efficient finetuning libaries ([PEFT](https://github.com/huggingface/peft)), and running inferences with containers in Vertex AI. There are various models supported in [PEFT](https://github.com/huggingface/peft). This notebook shows examples with some models, such as OpenLLaMA, Falcon-instruct, BERT, RoBERTa-large, and XLM-RoBERTa-large, etc.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Finetune causal language models with PEFT and run inferences with Vertex AI, supporting\n",
|
||||
"\n",
|
||||
"| Models | LoRA |\n",
|
||||
"| :- | :- |\n",
|
||||
"| [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b) | Y |\n",
|
||||
"| [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b) | Y |\n",
|
||||
"| [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) | Y |\n",
|
||||
"\n",
|
||||
"- Finetune instruct models with PEFT and run inferences with Vertex AI, supporting\n",
|
||||
"\n",
|
||||
"| Models | LoRA |\n",
|
||||
"| :- | :- |\n",
|
||||
"| [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct) | Y |\n",
|
||||
"| [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) | Y |\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"- Finetune sequence classification models with PEFT and run inferences with Vertex AI, supporting\n",
|
||||
"\n",
|
||||
"| Models | LoRA |\n",
|
||||
"| :- | :- |\n",
|
||||
"| [bert-base-uncase](https://huggingface.co/bert-base-uncased) | Y |\n",
|
||||
"| [RoBERTa-large](https://huggingface.co/roberta-large) | Y |\n",
|
||||
"| [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large) | Y |\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output.\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"EXPERIMENT_BUCKET = os.path.join(BUCKET_URI, \"peft\")\n",
|
||||
"DATA_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"data\")\n",
|
||||
"MODEL_BUCKET = os.path.join(EXPERIMENT_BUCKET, \"model\")\n",
|
||||
"\n",
|
||||
"# The service account looks like:\n",
|
||||
"# '@.iam.gserviceaccount.com'\n",
|
||||
"# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
|
||||
"# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
|
||||
"# The service account for deploying fine tuned model.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex AI API"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training and serving docker images.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-peft-train\"\n",
|
||||
"PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-peft-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" \"\"\"Gets the job name with date time when triggering training or deployment\n",
|
||||
" jobs in Vertex AI.\n",
|
||||
" \"\"\"\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(\n",
|
||||
" model_name,\n",
|
||||
" base_model_id,\n",
|
||||
" finetuned_lora_model_path,\n",
|
||||
" service_account,\n",
|
||||
" task,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
"):\n",
|
||||
" \"\"\"Deploys trained models into Vertex AI.\"\"\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"BASE_MODEL_ID\": base_model_id,\n",
|
||||
" \"FINETUNED_LORA_MODEL_PATH\": finetuned_lora_model_path,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/peft_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=service_account,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Casual Language Modeling + PEFT\n",
|
||||
"\n",
|
||||
"This section demonstrates how to finetune OpenLLaMA with PEFT LoRA, including [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "iWGwJHqI7LMs"
|
||||
},
|
||||
"source": [
|
||||
"### Finetune with LoRA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5qCrm_kJH5cz"
|
||||
},
|
||||
"source": [
|
||||
"Set the base model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "N3UBLiYrM3sU"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_model_id = \"openlm-research/open_llama_3b\" # @param [\"openlm-research/open_llama_3b\", \"openlm-research/open_llama_7b\", \"openlm-research/open_llama_13b\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KKEYoRfiHDVv"
|
||||
},
|
||||
"source": [
|
||||
"Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
|
||||
"\n",
|
||||
"This example uses the dataset [Abirate/english_quotes](https://huggingface.co/datasets/Abirate/english_quotes).\n",
|
||||
"\n",
|
||||
"In order to make the finetunig efficiently, we enabled quantization (8bits) when loading pretrained models for finetuning LoRA models. The peak GPU memory usages are ~7G, ~10G and ~16G for finetuning LoRA models for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) separately with default training parameters and the example dataset. In theory, open_llama_3b and open_llama_7b can be finetuned on 1 V100, and open_llama_13b can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65467b361315"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset_name = \"Abirate/english_quotes\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"# machine_type = \"n1-standard-8\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"replica_count = 1\n",
|
||||
"accelerator_count = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = get_job_name_with_datetime(\"openllama-lora-train\")\n",
|
||||
"train_job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
|
||||
"output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"train_job.run(\n",
|
||||
" args=[\n",
|
||||
" \"--task=causal-language-modeling-lora\",\n",
|
||||
" f\"--pretrained_model_id={base_model_id}\",\n",
|
||||
" f\"--dataset_name={dataset_name}\",\n",
|
||||
" f\"--output_dir={output_dir_gcsfuse}\",\n",
|
||||
" \"--lora_rank=16\",\n",
|
||||
" \"--lora_alpha=32\",\n",
|
||||
" \"--lora_dropout=0.05\",\n",
|
||||
" \"--warmup_steps=10\",\n",
|
||||
" \"--max_steps=10\",\n",
|
||||
" \"--learning_rate=2e-4\",\n",
|
||||
" ],\n",
|
||||
" replica_count=replica_count,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=accelerator_count,\n",
|
||||
" boot_disk_size_gb=500,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Trained models were saved in: \", output_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jqmCtkGnhDmp"
|
||||
},
|
||||
"source": [
|
||||
"### Run inferences with serving images\n",
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [openlm-research/open_llama_3b](https://huggingface.co/openlm-research/open_llama_3b), [openlm-research/open_llama_7b](https://huggingface.co/openlm-research/open_llama_7b), and [openlm-research/open_llama_13b](https://huggingface.co/openlm-research/open_llama_13b) with LoRA weights are ~5.3G, ~8.7G and ~15.2G separately with the default settings. We use V100 in deployments for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bf55e38815dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"openllama-peft-serve\"),\n",
|
||||
" base_model_id=base_model_id,\n",
|
||||
" finetuned_lora_model_path=output_dir,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"causal-language-modeling-lora\",\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name:\", endpoint.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4ab04da3ec9a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"Hi, Google.\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"for prediction in response.predictions[0]:\n",
|
||||
" print(prediction[\"generated_text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "af21a3cff1e0"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete custom train jobs.\n",
|
||||
"train_job.delete()\n",
|
||||
"\n",
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "W2USaHtQbE-l"
|
||||
},
|
||||
"source": [
|
||||
"## Instruct + PEFT\n",
|
||||
"\n",
|
||||
"This section demonstrates how to finetune Falcon-instruct with PEFT LoRA, including [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "PwluMqvYbLu9"
|
||||
},
|
||||
"source": [
|
||||
"### Finetune with LoRA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ap1pQd-ckAAb"
|
||||
},
|
||||
"source": [
|
||||
"Set the base model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vVTfG2bpbR90"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_model_id = \"tiiuae/falcon-7b-instruct\" # @param [\"tiiuae/falcon-7b-instruct\", \"tiiuae/falcon-40b-instruct\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "iJe_us8mkZEV"
|
||||
},
|
||||
"source": [
|
||||
"Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
|
||||
"\n",
|
||||
"This example uses the dataset [timdettmers/openassistant-guanaco](https://huggingface.co/datasets/timdettmers/openassistant-guanaco).\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages are ~11G and ~34G for finetuning LoRA models for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) separately with default training parameters and the example dataset. In theory, falcon-7b-instruct can be finetuned on 1 P100/V100, and falcon-40b-instruct can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "-i4M7mWPbV8s"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset_name = \"timdettmers/openassistant-guanaco\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"replica_count = 1\n",
|
||||
"accelerator_count = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = get_job_name_with_datetime(\"falcon-instruct-lora-train\")\n",
|
||||
"train_job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
|
||||
"output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"max_steps = 10\n",
|
||||
"train_job.run(\n",
|
||||
" args=[\n",
|
||||
" \"--task=instruct-lora\",\n",
|
||||
" f\"--pretrained_model_id={base_model_id}\",\n",
|
||||
" f\"--dataset_name={dataset_name}\",\n",
|
||||
" f\"--output_dir={output_dir_gcsfuse}\",\n",
|
||||
" \"--lora_rank=64\",\n",
|
||||
" \"--lora_alpha=16\",\n",
|
||||
" \"--lora_dropout=0.1\",\n",
|
||||
" \"--warmup_ratio=0.03\",\n",
|
||||
" f\"--max_steps={max_steps}\",\n",
|
||||
" \"--max_seq_length=512\",\n",
|
||||
" \"--learning_rate=2e-4\",\n",
|
||||
" ],\n",
|
||||
" replica_count=replica_count,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=accelerator_count,\n",
|
||||
" boot_disk_size_gb=500,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Trained models were saved in: \", output_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "oCgivsdllZ1x"
|
||||
},
|
||||
"source": [
|
||||
"### Run inferences with serving images"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "_lNIFvielcyV"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [tiiuae/falcon-7b-instruct](https://huggingface.co/tiiuae/falcon-7b-instruct), and [tiiuae/falcon-40b-instruct](https://huggingface.co/tiiuae/falcon-40b-instruct) with LoRA weights are ~15.5G and ~38.2G separately with the default settings. We use V100 in deployments for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "inrBXzoxk53t"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # If deploy finetuned falcon-40b-instruct models, please set\n",
|
||||
"# machine_type = \"a2-highgpu-1g\",\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"machine_type = \"n1-standard-8\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"falcon-peft-serve\"),\n",
|
||||
" base_model_id=base_model_id,\n",
|
||||
" finetuned_lora_model_path=os.path.join(output_dir, \"checkpoint-\" + str(max_steps)),\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"instruct-lora\",\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name: \", endpoint.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "_dmO3XooliAs"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed after the above model deployment step succeeds and before you run the next step below. Otherwise you might see a ServiceUnavailable: 503 502:Bad Gateway error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts.\n",
|
||||
"\n",
|
||||
"Example:\n",
|
||||
"\n",
|
||||
"```\n",
|
||||
"Human: What is a car?\n",
|
||||
"Assistant: A car, or a motor car, is a road-connected human-transportation system used to move people or goods from one place to another. The term also encompasses a wide range of vehicles, including motorboats, trains, and aircrafts. Cars typically have four wheels, a cabin for passengers, and an engine or motor. They have been around since the early 19th century and are now one of the most popular forms of transportation, used for daily commuting, shopping, and other purposes.\n",
|
||||
"```"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "JrKsrffGl14T"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"What is a car?\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"\n",
|
||||
"for prediction in response.predictions[0]:\n",
|
||||
" print(prediction[\"generated_text\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "uk-t69nFl9rr"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "CcIX2WAJmAI7"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete custom train jobs.\n",
|
||||
"train_job.delete()\n",
|
||||
"\n",
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "og_s64QVmJDb"
|
||||
},
|
||||
"source": [
|
||||
"## Sequence Classification + PEFT\n",
|
||||
"\n",
|
||||
"This section demonstrates how to finetune sequence classification models with PEFT LoRA, including [bert-base-uncased](https://huggingface.co/bert-base-uncased), [RoBERTa-large](https://huggingface.co/roberta-large), and [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "QqJcvCb2mn8l"
|
||||
},
|
||||
"source": [
|
||||
"### Finetune with PEFT"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c0E7dfqloeBB"
|
||||
},
|
||||
"source": [
|
||||
"Set the base model id."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "eru79ot6mtNG"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"base_model_id = \"xlm-roberta-large\" # @param [\"bert-base-uncased\", \"roberta-large\", \"xlm-roberta-large\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "y9n-qheaotS9"
|
||||
},
|
||||
"source": [
|
||||
"Use the Vertex AI SDK to create and run the custom training jobs with Vertex AI Model Garden training images.\n",
|
||||
"\n",
|
||||
"This example uses the dataset [glue](https://huggingface.co/datasets/glue).\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages are ~8.8G and ~15G for finetuning LoRA models for [RoBERTa-large](https://huggingface.co/roberta-large), and [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large) separately with default training parameters and the example dataset. In theory, RoBERTa-large can be finetuned on 1 P100/V100, and XLM-RoBERTa-large can be finetuned on 1 A100 (40G). We choose to use 1 A100 (40G) by default to support all these models in this notebook for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "OHeSYEKinfP3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset_name = \"glue\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"# # Please switch to A100 or other powerful machines if you run out of memory.\n",
|
||||
"# machine_type = \"a2-highgpu-1g\"\n",
|
||||
"# accelerator_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"machine_type = \"n1-standard-8\"\n",
|
||||
"accelerator_type = \"NVIDIA_TESLA_V100\"\n",
|
||||
"replica_count = 1\n",
|
||||
"accelerator_count = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = get_job_name_with_datetime(os.path.basename(base_model_id) + \"-lora-train\")\n",
|
||||
"train_job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"output_dir = os.path.join(MODEL_BUCKET, job_name)\n",
|
||||
"output_dir_gcsfuse = output_dir.replace(\"gs://\", \"/gcs/\")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"train_job.run(\n",
|
||||
" args=[\n",
|
||||
" \"--task=sequence-classification-lora\",\n",
|
||||
" f\"--pretrained_model_id={base_model_id}\",\n",
|
||||
" f\"--dataset_name={dataset_name}\",\n",
|
||||
" f\"--output_dir={output_dir_gcsfuse}\",\n",
|
||||
" \"--lora_rank=8\",\n",
|
||||
" \"--lora_alpha=16\",\n",
|
||||
" \"--lora_dropout=0.1\",\n",
|
||||
" \"--num_epochs=20\",\n",
|
||||
" \"--batch_size=32\",\n",
|
||||
" \"--learning_rate=3e-4\",\n",
|
||||
" ],\n",
|
||||
" replica_count=replica_count,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=accelerator_type,\n",
|
||||
" accelerator_count=accelerator_count,\n",
|
||||
" boot_disk_size_gb=500,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"Trained models were saved in: \", output_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kJuHaBCqoacg"
|
||||
},
|
||||
"source": [
|
||||
"### Run inferences with serving images"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "jw1GFXydoft0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"The peak GPU memory usages for [RoBERTa-large](https://huggingface.co/roberta-large), and [XLM-RoBERTa-large](https://huggingface.co/xlm-roberta-large) with LoRA weights are ~2G and ~2.4G separately with the default settings. We use V100 in deployments for simplicity."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "A5j0a3AZoj3h"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_name=get_job_name_with_datetime(prefix=\"sequence-classification-peft-serve\"),\n",
|
||||
" base_model_id=base_model_id,\n",
|
||||
" finetuned_lora_model_path=output_dir,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" task=\"sequence-classification-lora\",\n",
|
||||
")\n",
|
||||
"print(\"endpoint_name:\", endpoint.name)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6dR3-Hc7oqu-"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed after the above model deployment step succeeds and before you run the next step below. Otherwise you might see a ServiceUnavailable: 503 502:Bad Gateway error when you send requests to the endpoint.\n",
|
||||
"\n",
|
||||
"Once deployment succeeds, you can send requests to the endpoint with text prompts."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "xAmULPegoyXl"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# # Loads an existing endpoint as below.\n",
|
||||
"# endpoint_name = endpoint.name\n",
|
||||
"# aip_endpoint_name = (\n",
|
||||
"# f\"projects/{PROJECT_ID}/locations/{REGION}/endpoints/{endpoint_name}\"\n",
|
||||
"# )\n",
|
||||
"# endpoint = aiplatform.Endpoint(aip_endpoint_name)\n",
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"The cat sat on the mat.\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"labels = [int(item) for item in response.predictions]\n",
|
||||
"print(labels)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1Vg_b46Jo6C1"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vNiTDL_VpAlp"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete custom train jobs.\n",
|
||||
"train_job.delete()\n",
|
||||
"\n",
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_peft.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ad41232f-4ac9-4607-8ee6-9b1d98d0f5c3",
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "30e2cb63",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Pic2Word\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pic2word.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_jpic2word.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> <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pic2word.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",
|
||||
"id": "5d5af88c-a3c8-46fa-b90a-1b6737a91534",
|
||||
"metadata": {
|
||||
"id": "7e3e5205fbfd"
|
||||
},
|
||||
"source": [
|
||||
"## Overview"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "87b71198-5c24-4e8c-aab1-44e080aa7fe7",
|
||||
"metadata": {
|
||||
"id": "523084ce4894"
|
||||
},
|
||||
"source": [
|
||||
"This notebook demonstrates how to use the [Pic2Word](https://github.com/google-research/composed_image_retrieval) model in Vertex AI Model Garden. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "0ba32ec8-e06f-474f-b753-20aaf74ebd12",
|
||||
"metadata": {
|
||||
"id": "f9cbeb1704e1"
|
||||
},
|
||||
"source": [
|
||||
"## Objective"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "95694bc4-0626-4371-8b2a-6b89967742db",
|
||||
"metadata": {
|
||||
"id": "da71cd41e69e"
|
||||
},
|
||||
"source": [
|
||||
"Following the notebook you will conduct experiments using the pre-built docker image on Vertex AI.\n",
|
||||
"\n",
|
||||
"- Deploy pretrained Pic2Word models in Google Cloud Vertex AI\n",
|
||||
"\n",
|
||||
"This tutorial uses the following Google Cloud ML services and resources:\n",
|
||||
"\n",
|
||||
"- Vertex AI Model Registry\n",
|
||||
"- Vertex AI Online Prediction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "b1893e0c-859b-4d42-b1d8-d133855d8767",
|
||||
"metadata": {
|
||||
"id": "8264c04e0f34"
|
||||
},
|
||||
"source": [
|
||||
"## Dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "a099439c-3122-429b-8215-caaf9aee69c5",
|
||||
"metadata": {
|
||||
"id": "52e4f9df87b2"
|
||||
},
|
||||
"source": [
|
||||
"We use the [COCO](https://cocodataset.org/#home) validation set (5,000 images) for evaluation."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "56d2a9f8-2d99-407e-a378-57f99754185b",
|
||||
"metadata": {
|
||||
"id": "169ef74e43a6"
|
||||
},
|
||||
"source": [
|
||||
"## Costs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "bb62de0d-71ac-45ee-82ac-5f7465f2f0eb",
|
||||
"metadata": {
|
||||
"id": "61a64432ac87"
|
||||
},
|
||||
"source": [
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"- Vertex AI\n",
|
||||
"- Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing?_ga=2.46650789.-341051769.1686949237) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing?_ga=2.46650789.-341051769.1686949237), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/?_ga=2.247379078.-341051769.1686949237) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "ea27403e",
|
||||
"metadata": {
|
||||
"id": "586c9147ba8a"
|
||||
},
|
||||
"source": [
|
||||
"# Installation\n",
|
||||
"\n",
|
||||
"Install the following packages required to execute this notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f9c685d9",
|
||||
"metadata": {
|
||||
"id": "68f1241e68a7"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" # Configs for colab notebooks.\n",
|
||||
" ! pip3 install --upgrade --quiet google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "e75fe500-5dc1-4ab3-8829-3eeada5be7f7",
|
||||
"metadata": {
|
||||
"id": "d0c7bca32b78"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "e8b0ef65",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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 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 need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with **Vertex AI User** and **Storage Object Admin** roles for deploying fine tuned model to Vertex AI endpoint.\n",
|
||||
"\n",
|
||||
"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.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with ! as shell commands, and it interpolates Python variables prefixed with $ into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "09490824",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**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,
|
||||
"id": "6ddc1d11",
|
||||
"metadata": {
|
||||
"id": "oM1iC_MfAts1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"your-project-id\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "9ed7d668",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### 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)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "90d0108f",
|
||||
"metadata": {
|
||||
"id": "twgKk-LsLmX3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "ca43938d",
|
||||
"metadata": {
|
||||
"id": "409f1effab6c"
|
||||
},
|
||||
"source": [
|
||||
"### Buckets\n",
|
||||
"\n",
|
||||
"You can create a storage bucket to store model input and output images."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "889d10ac",
|
||||
"metadata": {
|
||||
"id": "bcdadd216c34"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The form for BUCKET_URI is gs://.\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"INPUT_BUCKET = os.path.join(BUCKET_URI, \"/input\")\n",
|
||||
"OUTPUT_BUCKET = os.path.join(BUCKET_URI, \"/output\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "15c0e671",
|
||||
"metadata": {
|
||||
"id": "sBCra4QMA2wR"
|
||||
},
|
||||
"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",
|
||||
"id": "623a03cc",
|
||||
"metadata": {
|
||||
"id": "74ccc9e52986"
|
||||
},
|
||||
"source": [
|
||||
"**1. Vertex AI Workbench**\n",
|
||||
"* Do nothing as you are already authenticated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "fbee4160",
|
||||
"metadata": {
|
||||
"id": "de775a3773ba"
|
||||
},
|
||||
"source": [
|
||||
"**2. Local JupyterLab instance, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3eabaf6b",
|
||||
"metadata": {
|
||||
"id": "254614fa0c46"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ! gcloud auth login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "90a615fe",
|
||||
"metadata": {
|
||||
"id": "ef21552ccea8"
|
||||
},
|
||||
"source": [
|
||||
"**3. Colab, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "3d4e50e1",
|
||||
"metadata": {
|
||||
"id": "603adbbf0532"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# from google.colab import auth\n",
|
||||
"# auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "b0b40233",
|
||||
"metadata": {
|
||||
"id": "f6b2ccc891ed"
|
||||
},
|
||||
"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",
|
||||
"id": "a8019fcc-26ce-4536-b394-aa1fb18794ee",
|
||||
"metadata": {
|
||||
"id": "4226467373ef"
|
||||
},
|
||||
"source": [
|
||||
"If you are running this notebook locally, you will need to install the [Cloud SDK](https://cloud.google.com/sdk) and [gsutil](https://cloud.google.com/storage/docs/gsutil_install)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "315598c1-3ff4-4bc8-9b82-7e2fe55d4678",
|
||||
"metadata": {
|
||||
"id": "8ba36d3a171d"
|
||||
},
|
||||
"source": [
|
||||
"### Setup variables"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "6d26ddb2-04ad-4973-8965-9b25eb24993f",
|
||||
"metadata": {
|
||||
"id": "25ca675e59e7"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Prediction constants.\n",
|
||||
"PREDICTION_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pic2word_serve:latest\"\n",
|
||||
"PREDICTION_ACCELERATOR_TYPE = \"NVIDIA_TESLA_T4\"\n",
|
||||
"PREDICTION_MACHINE_TYPE = \"n1-standard-8\"\n",
|
||||
"\n",
|
||||
"# The serving port.\n",
|
||||
"SERVE_PORT = 7080\n",
|
||||
"\n",
|
||||
"# The service account looks like:\n",
|
||||
"# '@.iam.gserviceaccount.com'\n",
|
||||
"# Please go to https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console\n",
|
||||
"# and create service account with `Vertex AI User` and `Storage Object Admin` roles.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "0daec6e5-7642-458f-bd93-60a15431b0a1",
|
||||
"metadata": {
|
||||
"id": "71dd15118703"
|
||||
},
|
||||
"source": [
|
||||
"## Deploy model for online prediction"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "b286ce6b-cb00-441b-8249-93497de17974",
|
||||
"metadata": {
|
||||
"id": "5c891f6352ad"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Vertex Model Registry and deploys it on an Endpoint resource. This will take around 15 minutes to finish."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "01f686b2-9227-496b-9c4d-a63becb59fb8",
|
||||
"metadata": {
|
||||
"id": "e6abf4ee450a"
|
||||
},
|
||||
"source": [
|
||||
"### Upload and deploy model to Vertex AI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8885d5be-5007-4778-9562-976102fefe73",
|
||||
"metadata": {
|
||||
"id": "645d8d1df8d4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# Init common setup.\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=BUCKET_URI)\n",
|
||||
"\n",
|
||||
"# Upload model.\n",
|
||||
"serving_env = {}\n",
|
||||
"model = aiplatform.Model.upload(\n",
|
||||
" display_name=\"pic2word-model\",\n",
|
||||
" serving_container_image_uri=PREDICTION_DOCKER_URI,\n",
|
||||
" serving_container_ports=[SERVE_PORT],\n",
|
||||
" serving_container_predict_route=\"/predictions/pic2word\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
")\n",
|
||||
"# Or reuse a pre-uploaded model.\n",
|
||||
"# model = aiplatform.Model('projects/123456789/locations/us-central1/models/123456789@1')\n",
|
||||
"\n",
|
||||
"# Create an endpoint.\n",
|
||||
"endpoint = aiplatform.Endpoint.create(display_name=\"pytorch-pic2word-endpoint\")\n",
|
||||
"# Or reuse a pre-created endpoint.\n",
|
||||
"# endpoint = aiplatform.Endpoint('projects/123456789/locations/us-central1/endpoints/123456789')\n",
|
||||
"\n",
|
||||
"# Deploy model to endpoint.\n",
|
||||
"model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=PREDICTION_MACHINE_TYPE,\n",
|
||||
" accelerator_type=PREDICTION_ACCELERATOR_TYPE,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" traffic_percentage=100,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "f0bd36bb-f988-479a-be40-18c54980ca1f",
|
||||
"metadata": {
|
||||
"id": "2bb47a2855a2"
|
||||
},
|
||||
"source": [
|
||||
"You can manage your uploaded models in the [Model Registry](https://pantheon.corp.google.com/vertex-ai/models) and your endpoints in the [Endpoints](https://pantheon.corp.google.com/vertex-ai/endpoints)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "95edbcdd-8246-444a-aef2-a9cadbc03000",
|
||||
"metadata": {
|
||||
"id": "f14ff5e7a476"
|
||||
},
|
||||
"source": [
|
||||
"## Send a prediction request to the endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "3a83d6bb",
|
||||
"metadata": {
|
||||
"id": "c41c7d51d654"
|
||||
},
|
||||
"source": [
|
||||
"Set the query text and upload your desired query image to the specified INPUT_BUCKET. Use an asterisk in the query text to specify which token gets replaced by the image token. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f7c6fc99-42cd-4544-86df-b14f49189bfd",
|
||||
"metadata": {
|
||||
"id": "58d4f8e84e02"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"payload = json.dumps(\n",
|
||||
" {\n",
|
||||
" \"query\": \"a bunch of *\",\n",
|
||||
" \"image_path\": INPUT_BUCKET,\n",
|
||||
" \"image_file_name\": \"cat.png\",\n",
|
||||
" \"output_storage_dir\": OUTPUT_BUCKET,\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"response = endpoint.predict(payload).predictions\n",
|
||||
"print(response)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"id": "8e71ee23-967c-4871-802f-ccb39958386e",
|
||||
"metadata": {
|
||||
"id": "608b936179df"
|
||||
},
|
||||
"source": [
|
||||
"## Cleaning Up\n",
|
||||
"\n",
|
||||
"To clean up all Google Cloud resources used in this project, you can delete the Google Cloud project you used for the tutorial.\n",
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d9be2a18-db11-4108-9622-647b33be2594",
|
||||
"metadata": {
|
||||
"id": "7d17b385141d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Delete endpoint resource.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete model resource.\n",
|
||||
"model.delete()\n",
|
||||
"\n",
|
||||
"# Delete Cloud Storage objects that were created.\n",
|
||||
"delete_bucket = False\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil -m rm -r $BUCKET_URI"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_pic2word.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6ad30fe2-1fc1-47e3-8a9f-624170b5aae6"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cYYdTBQoKeCP"
|
||||
},
|
||||
"source": [
|
||||
" # Vertex AI Model Garden - Segment Anything Model (SAM) Serving on Vertex AI\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_sam.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_sam.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_sam.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "JbmPgTp2LRCY"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates using the [huggingface/transformers](https://github.com/huggingface/transformers) framework to serve Segment Anything Model (SAM) models and deploy them for online prediction on Vertex AI.\n",
|
||||
"\n",
|
||||
"Following the notebook you will conduct experiments using the pre-built docker image on Vertex AI.\n",
|
||||
"\n",
|
||||
"- With the pre-built docker images, you can **deploy** models for the following tasks:\n",
|
||||
" - Mask Generation\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "78f72e0a-52e5-4de5-ac0f-2171b3493825"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "G__krby2Mqmh"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "RS5_6QVFMyc-"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip3 install --upgrade google-cloud-aiplatform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Li2aj1n9NDYl"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
"google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f6c1bc20-3495-448a-b242-01930ba8153c"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5b1f2c08-c84e-4158-a976-e6b59d1055ca"
|
||||
},
|
||||
"source": [
|
||||
"It's highly recommended to run this notebook on [Vertex AI workbench](https://cloud.google.com/vertex-ai-workbench)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "4fc1fc14-2d77-4bf7-8f6d-c1afc10c848a"
|
||||
},
|
||||
"source": [
|
||||
"If you are running this notebook locally, you will need to install the [Cloud SDK](https://cloud.google.com/sdk) and [gsutil](https://cloud.google.com/storage/docs/gsutil_install)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e02b9811-b730-4573-83fa-9d47f2ce0436"
|
||||
},
|
||||
"source": [
|
||||
"####Fill following variables for experiments environment:\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7c2ce2fa-5a9b-40f6-b99d-6c1325775b36"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fwgeVkFXOS64"
|
||||
},
|
||||
"source": [
|
||||
"*Initialize* Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "MxqnJMahPK8r"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tSjMVyG8PYS-"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "H05BzPO5Pcnh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image.\n",
|
||||
"# The model artifacts are embedded within the container, except for model weights which will be downloaded during deployment.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/sam-serve\"\n",
|
||||
"\n",
|
||||
"# The serving port.\n",
|
||||
"SERVE_PORT = 7080\n",
|
||||
"\n",
|
||||
"# The serving route.\n",
|
||||
"SERVE_ROUTE = \"/predictions/sam_serving\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "82058975-23b5-4b97-9e14-dd9a29c578ed"
|
||||
},
|
||||
"source": [
|
||||
"### Define common utility functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "a78f988d-e5e2-4a57-ba0f-569e970514c0"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import matplotlib.patches as patches\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy as np\n",
|
||||
"import pycocotools.mask as mask_util\n",
|
||||
"import requests\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=\"JPEG\")\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def draw_image_with_boxes(image, boxes):\n",
|
||||
" fig, ax = plt.subplots()\n",
|
||||
" ax.imshow(image)\n",
|
||||
" for box in boxes:\n",
|
||||
" x, y = box[\"xmin\"], box[\"ymin\"]\n",
|
||||
" width, height = box[\"xmax\"] - x, box[\"ymax\"] - y\n",
|
||||
" rect = patches.Rectangle(\n",
|
||||
" (x, y), width, height, linewidth=2, edgecolor=\"yellow\", facecolor=\"none\"\n",
|
||||
" )\n",
|
||||
" ax.add_patch(rect)\n",
|
||||
" plt.axis(\"off\")\n",
|
||||
" plt.show()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def decode_rle_masks(pred_masks_rle):\n",
|
||||
" return np.stack([mask_util.decode(rle) for rle in pred_masks_rle])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def show_mask(mask, ax, random_color=False):\n",
|
||||
" if random_color:\n",
|
||||
" color = np.concatenate([np.random.random(3), np.array([0.6])], axis=0)\n",
|
||||
" else:\n",
|
||||
" color = np.array([30 / 255, 144 / 255, 255 / 255, 0.6])\n",
|
||||
" h, w = mask.shape[-2:]\n",
|
||||
" mask_image = mask.reshape(h, w, 1) * color.reshape(1, 1, -1)\n",
|
||||
" ax.imshow(mask_image)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def show_predictions(preds):\n",
|
||||
" # create figure\n",
|
||||
" fig = plt.figure(figsize=(10, 7))\n",
|
||||
"\n",
|
||||
" fig.add_subplot(1, 2, 1)\n",
|
||||
" plt.imshow(np.array(image1))\n",
|
||||
" ax = plt.gca()\n",
|
||||
" masks = decode_rle_masks(preds[0][\"masks_rle\"])\n",
|
||||
" for mask in masks:\n",
|
||||
" show_mask(mask, ax=ax, random_color=True)\n",
|
||||
" plt.axis(\"off\")\n",
|
||||
"\n",
|
||||
" fig.add_subplot(1, 2, 2)\n",
|
||||
" plt.imshow(np.array(image2))\n",
|
||||
" ax = plt.gca()\n",
|
||||
" masks = decode_rle_masks(preds[1][\"masks_rle\"])\n",
|
||||
" for mask in masks:\n",
|
||||
" show_mask(mask, ax=ax, random_color=True)\n",
|
||||
" plt.axis(\"off\")\n",
|
||||
" plt.show()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(task, model_id):\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{task}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=task,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[SERVE_PORT],\n",
|
||||
" serving_container_predict_route=SERVE_ROUTE,\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return endpoint, model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "242fc8cf-5a0e-483b-8c9d-77a474cedc4b"
|
||||
},
|
||||
"source": [
|
||||
"## Mask Generation with Vertex AI Endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "f3f9ed94d69c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"endpoint, model = deploy_model(\n",
|
||||
" task=\"mask-generation\", model_id=\"facebook/sam-vit-large\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "d6e51c57-b5e2-4ae7-888a-5391cceee5fb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image1 = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"image2 = download_image(\"http://images.cocodataset.org/val2017/000000000285.jpg\")\n",
|
||||
"grid = image_grid([image1, image2], 1, 2)\n",
|
||||
"display(grid)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image1)},\n",
|
||||
" {\"image\": image_to_base64(image2)},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"show_predictions(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5UdquXMWR1E4"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "TGTWIJK8R136"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_sam.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,793 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Stable Diffusion V1.5\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion.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",
|
||||
" (a Python-3 GPU notebook with preinstalled HuggingFace/transformer libraries is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates running local inference for [runwayml/stable-diffusion-v1-5](https://huggingface.co/runwayml/stable-diffusion-v1-5) on either [Colab](https://colab.research.google.com) or [Vertex AI Workbench](https://cloud.google.com/vertex-ai-workbench). This notebook also demonstrates finetuning runwayml/stable-diffusion-v1-5 with [Dreambooth](https://huggingface.co/docs/diffusers/training/dreambooth) and deploying it on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Run local predictions for text-to-image and text-guided-image-to-image with serving dockers.\n",
|
||||
"- Finetune the stable-diffusion-v1.5 model with [Dreambooth](https://huggingface.co/docs/diffusers/training/dreambooth).\n",
|
||||
"- Upload the model to [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model to a [Vertex AI Endpoint resource](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for text-to-image and text-guided-image-to-image.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"\n",
|
||||
"**NOTE**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Setup notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"#### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" ! pip3 install --upgrade pip\n",
|
||||
" ! pip3 install torchvision==0.14.1\n",
|
||||
" ! pip3 install transformers==4.27.1\n",
|
||||
" ! pip3 install diffusers==0.15.1\n",
|
||||
" ! pip3 install datasets==2.9.0\n",
|
||||
" ! pip3 install accelerate==0.18.0\n",
|
||||
" ! pip3 install triton==2.0.0.dev20221120\n",
|
||||
" ! pip3 install xformers==0.0.16\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
" # Remove wrong cublas version.\n",
|
||||
" ! pip3 uninstall nvidia_cublas_cu11 --yes\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fb671e75ca7b"
|
||||
},
|
||||
"source": [
|
||||
"#### Workbench only\n",
|
||||
"1. Follow [this link](https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.ipynb) to deploy the notebook to a Vertex AI Workbench Instance.\n",
|
||||
"2. Select `Create a new Notebook`.\n",
|
||||
"3. Click `Advanced Options`.\n",
|
||||
"4. In the **Environment** tab, select `Debian 10` for **Operating System** and select `Custom Container` for **Environment**.\n",
|
||||
"5. Set `Docker container image` to `us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/transformers-notebook`.\n",
|
||||
"6. Under **Machine configuration**, select 1 `T4` GPU and select `Install NVIDIA GPU driver automatically for me`.\n",
|
||||
"7. Click `Create` to create the Vertex AI Workbench instance."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The service account for deploying fine tuned model.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex-AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training docker image. It contains training scripts and models.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-train:latest\"\n",
|
||||
"\n",
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import glob\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform, storage\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"stable-diffusion-v1\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-{task}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" )\n",
|
||||
" return model, endpoint\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_bucket_and_blob_name(filepath):\n",
|
||||
" # The gcs path is of the form gs://<bucket-name>/<blob-name>\n",
|
||||
" gs_suffix = filepath.split(\"gs://\", 1)[1]\n",
|
||||
" return tuple(gs_suffix.split(\"/\", 1))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_local_dir_to_gcs(local_dir_path, gcs_dir_path):\n",
|
||||
" \"\"\"Uploads files in a local directory to a GCS directory.\"\"\"\n",
|
||||
" client = storage.Client()\n",
|
||||
" bucket_name = gcs_dir_path.split(\"/\")[2]\n",
|
||||
" bucket = client.get_bucket(bucket_name)\n",
|
||||
" for local_file in glob.glob(local_dir_path + \"/**\"):\n",
|
||||
" if not os.path.isfile(local_file):\n",
|
||||
" continue\n",
|
||||
" filename = local_file[1 + len(local_dir_path) :]\n",
|
||||
" gcs_file_path = os.path.join(gcs_dir_path, filename)\n",
|
||||
" _, blob_name = get_bucket_and_blob_name(gcs_file_path)\n",
|
||||
" blob = bucket.blob(blob_name)\n",
|
||||
" blob.upload_from_filename(local_file)\n",
|
||||
" print(\"Copied {} to {}.\".format(local_file, gcs_file_path))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e8a42fa49305"
|
||||
},
|
||||
"source": [
|
||||
"## Run inferences locally"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1d5ebc91c786"
|
||||
},
|
||||
"source": [
|
||||
"### Text-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "d39ed8c97cc5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionPipeline\n",
|
||||
"\n",
|
||||
"model_id = \"runwayml/stable-diffusion-v1-5\"\n",
|
||||
"pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16)\n",
|
||||
"pipe = pipe.to(\"cuda\")\n",
|
||||
"\n",
|
||||
"prompt = \"a photo of an astronaut riding a horse on mars\"\n",
|
||||
"\n",
|
||||
"results = pipe(prompt=prompt, guidance_scale=7.5)\n",
|
||||
"images = results.images\n",
|
||||
"nsfw_detects = results.nsfw_content_detected\n",
|
||||
"display(images[0])\n",
|
||||
"print(nsfw_detects[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5aed5ed7b6f6"
|
||||
},
|
||||
"source": [
|
||||
"### Text-guided image-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "faadabb7728f"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionImg2ImgPipeline\n",
|
||||
"\n",
|
||||
"device = \"cuda\"\n",
|
||||
"model_id_or_path = \"runwayml/stable-diffusion-v1-5\"\n",
|
||||
"pipe = StableDiffusionImg2ImgPipeline.from_pretrained(\n",
|
||||
" model_id_or_path, torch_dtype=torch.float16\n",
|
||||
")\n",
|
||||
"pipe = pipe.to(device)\n",
|
||||
"\n",
|
||||
"url = \"https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg\"\n",
|
||||
"\n",
|
||||
"response = requests.get(url)\n",
|
||||
"init_image = Image.open(BytesIO(response.content)).convert(\"RGB\")\n",
|
||||
"init_image = init_image.resize((768, 512))\n",
|
||||
"\n",
|
||||
"prompt = \"A fantasy landscape, trending on artstation\"\n",
|
||||
"\n",
|
||||
"results = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5)\n",
|
||||
"images = results.images\n",
|
||||
"nsfw_detects = results.nsfw_content_detected\n",
|
||||
"display(images[0])\n",
|
||||
"print(nsfw_detects[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune with Dreambooth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0dc65d8f0689"
|
||||
},
|
||||
"source": [
|
||||
"This section uses [dreambooth](https://dreambooth.github.io/) to finetune the [stable-diffusion-v1.5](https://huggingface.co/runwayml/stable-diffusion-v1-5) model with [5 dog images](https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ) to personalize the text-to-image model.\n",
|
||||
"\n",
|
||||
"It finetunes both text encoder and unet of the stable diffusion model up to 800 steps. The whole finetuning job takes 30 minutes to finish using 1 A100 GPU.\n",
|
||||
"\n",
|
||||
"The full model will be saved after the finetuning job finishs and it can be loaded by the [StableDiffusionPipeline](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/text2img) to run inference."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "34048707df5c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Download example training images.\n",
|
||||
"!gdown --folder https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ\n",
|
||||
"\n",
|
||||
"# Upload data to Cloud Storage bucket.\n",
|
||||
"upload_local_dir_to_gcs(\"dog\", f\"gs://{GCS_BUCKET}/dreambooth/dog\")\n",
|
||||
"upload_local_dir_to_gcs(\"dog\", f\"gs://{GCS_BUCKET}/dreambooth/dog_class\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "969cfeb79317"
|
||||
},
|
||||
"source": [
|
||||
"**NOTE**: If the upload step fails due to lacking of permission, you need to [grant the Storage Object Admin role](https://cloud.google.com/storage/docs/access-control/using-iam-permissions) for the Cloud account of the notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65467b361315"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-trained model to be loaded.\n",
|
||||
"model_id = \"runwayml/stable-diffusion-v1-5\"\n",
|
||||
"\n",
|
||||
"# Input and output path.\n",
|
||||
"instance_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/dog\"\n",
|
||||
"class_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/dog_class\"\n",
|
||||
"output_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/output\"\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"num_nodes = 1\n",
|
||||
"gpu_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"num_gpus = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = create_job_name(\"dreambooth-stable-diffusion\")\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"# See https://github.com/huggingface/diffusers/blob/v0.14.0/examples/dreambooth/train_dreambooth.py#L75\n",
|
||||
"# for a full list of training arguments.\n",
|
||||
"model = job.run(\n",
|
||||
" args=[\n",
|
||||
" \"dreambooth/train_dreambooth.py\",\n",
|
||||
" f\"--pretrained_model_name_or_path={model_id}\",\n",
|
||||
" \"--train_text_encoder\",\n",
|
||||
" f\"--instance_data_dir={instance_dir}\",\n",
|
||||
" f\"--class_data_dir={class_dir}\",\n",
|
||||
" f\"--output_dir={output_dir}\",\n",
|
||||
" \"--with_prior_preservation\",\n",
|
||||
" \"--prior_loss_weight=1.0\",\n",
|
||||
" \"--instance_prompt='a photo of sks dog'\",\n",
|
||||
" \"--class_prompt='a photo of dog'\",\n",
|
||||
" \"--resolution=512\",\n",
|
||||
" \"--train_batch_size=1\",\n",
|
||||
" \"--gradient_checkpointing\",\n",
|
||||
" \"--learning_rate=2e-6\",\n",
|
||||
" \"--lr_scheduler=constant\",\n",
|
||||
" \"--lr_warmup_steps=0\",\n",
|
||||
" \"--num_class_images=200\",\n",
|
||||
" \"--max_train_steps=800\",\n",
|
||||
" ],\n",
|
||||
" replica_count=num_nodes,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=gpu_type,\n",
|
||||
" accelerator_count=num_gpus,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bf7f82732e61"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and Deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1cc26e68d7b0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cd7b56421392"
|
||||
},
|
||||
"source": [
|
||||
"### Text-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6d331b1ea337"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the stable diffusion model for the text-to-image task.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send a batch of text prompts to the endpoint to generated images.\n",
|
||||
"\n",
|
||||
"When deployed on one V100 GPU, the averaged inference time of a request is ~15 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bf55e38815dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the model_id to \"runwayml/stable-diffusion-v1-5\" to load the OSS pre-trained model.\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=f\"gs://{GCS_BUCKET}/dreambooth/output\", task=\"text-to-image\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4ab04da3ec9a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"a squirrel in Picasso style\"},\n",
|
||||
" {\"prompt\": \"a dog in Picasso style\"},\n",
|
||||
" {\"prompt\": \"a cat in Picasso style\"},\n",
|
||||
" {\"prompt\": \"a deer in Picasso style\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"image_grid(images)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "af21a3cff1e0"
|
||||
},
|
||||
"source": [
|
||||
"Clean up resources:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c1e51f764a60"
|
||||
},
|
||||
"source": [
|
||||
"### Text-guided image-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fa686a54047c"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the stable diffusion model for the text-guided image-to-image task."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65e32356fbd1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the model_id to a GCS path, like \"gs://GCS_BUCKET/dreambooth/output\", to load the dreambooth finetuned model above.\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"runwayml/stable-diffusion-v1-5\", task=\"image-to-image\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "SsjYFLvNymc0"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "83a50fd4a1ed"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"init_image = download_image(\n",
|
||||
" \"https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg\"\n",
|
||||
")\n",
|
||||
"display(init_image)\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"A fantasy landscape, trending on artstation\",\n",
|
||||
" \"image\": image_to_base64(init_image),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ed3795d474b9"
|
||||
},
|
||||
"source": [
|
||||
"Clean up resources:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b53b883257b4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_stable_diffusion.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,770 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Stable Diffusion V2.1\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_2_1.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_2_1.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_2_1.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",
|
||||
" (a Python-3 GPU notebook with preinstalled HuggingFace/transformer libraries is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates running local inference for [stabilityai/stable-diffusion-2-1](https://huggingface.co/stabilityai/stable-diffusion-2-1) on either [Colab](https://colab.research.google.com) or [Vertex AI Workbench](https://cloud.google.com/vertex-ai-workbench). This notebook also demonstrates finetuning stabilityai/stable-diffusion-2-1 with [Dreambooth](https://huggingface.co/docs/diffusers/training/dreambooth) and deploying it on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Run local predictions for text-to-image and text-guided-image-to-image with serving dockers.\n",
|
||||
"- Finetune the stabilityai/stable-diffusion-2-1 model with [Dreambooth](https://huggingface.co/docs/diffusers/training/dreambooth).\n",
|
||||
"- Upload the model to [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model to a [Vertex AI Endpoint resource](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for text-to-image and text-guided-image-to-image.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Setup notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"#### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" ! pip3 install --upgrade pip\n",
|
||||
" ! pip3 install torchvision==0.14.1\n",
|
||||
" ! pip3 install transformers==4.27.1\n",
|
||||
" ! pip3 install diffusers==0.15.1\n",
|
||||
" ! pip3 install datasets==2.9.0\n",
|
||||
" ! pip3 install accelerate==0.18.0\n",
|
||||
" ! pip3 install triton==2.0.0.dev20221120\n",
|
||||
" ! pip3 install xformers==0.0.16\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
" # Remove wrong cublas version.\n",
|
||||
" ! pip3 uninstall nvidia_cublas_cu11 --yes\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fb671e75ca7b"
|
||||
},
|
||||
"source": [
|
||||
"#### Workbench only\n",
|
||||
"1. Follow [this link](https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.ipynb) to deploy the notebook to a Vertex AI Workbench Instance.\n",
|
||||
"2. Select `Create a new Notebook`.\n",
|
||||
"3. Click `Advanced Options`.\n",
|
||||
"4. In the **Environment** tab, select `Debian 10` for **Operating System** and select `Custom Container` for **Environment**.\n",
|
||||
"5. Set `Docker container image` to `us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/transformers-notebook`.\n",
|
||||
"6. Under **Machine configuration**, select 1 `T4` GPU and select `Install NVIDIA GPU driver automatically for me`.\n",
|
||||
"7. Click `Create` to create the Vertex AI Workbench instance."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The service account for deploying fine tuned model.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex-AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training docker image. It contains training scripts and models.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-train:latest\"\n",
|
||||
"\n",
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import glob\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform, storage\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"stable-diffusion-v2\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-{task}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" )\n",
|
||||
" return model, endpoint\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_bucket_and_blob_name(filepath):\n",
|
||||
" # The gcs path is of the form gs://<bucket-name>/<blob-name>\n",
|
||||
" gs_suffix = filepath.split(\"gs://\", 1)[1]\n",
|
||||
" return tuple(gs_suffix.split(\"/\", 1))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_local_dir_to_gcs(local_dir_path, gcs_dir_path):\n",
|
||||
" \"\"\"Uploads files in a local directory to a GCS directory.\"\"\"\n",
|
||||
" client = storage.Client()\n",
|
||||
" bucket_name = gcs_dir_path.split(\"/\")[2]\n",
|
||||
" bucket = client.get_bucket(bucket_name)\n",
|
||||
" for local_file in glob.glob(local_dir_path + \"/**\"):\n",
|
||||
" if not os.path.isfile(local_file):\n",
|
||||
" continue\n",
|
||||
" filename = local_file[1 + len(local_dir_path) :]\n",
|
||||
" gcs_file_path = os.path.join(gcs_dir_path, filename)\n",
|
||||
" _, blob_name = get_bucket_and_blob_name(gcs_file_path)\n",
|
||||
" blob = bucket.blob(blob_name)\n",
|
||||
" blob.upload_from_filename(local_file)\n",
|
||||
" print(\"Copied {} to {}.\".format(local_file, gcs_file_path))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e8a42fa49305"
|
||||
},
|
||||
"source": [
|
||||
"## Run inferences locally"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1d5ebc91c786"
|
||||
},
|
||||
"source": [
|
||||
"### Text-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "d39ed8c97cc5"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import DPMSolverMultistepScheduler, StableDiffusionPipeline\n",
|
||||
"\n",
|
||||
"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
|
||||
"model_id = \"stabilityai/stable-diffusion-2-1\"\n",
|
||||
"pipe = StableDiffusionPipeline.from_pretrained(model_id, torch_dtype=torch.float16)\n",
|
||||
"pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)\n",
|
||||
"pipe = pipe.to(device)\n",
|
||||
"pipe.enable_attention_slicing()\n",
|
||||
"\n",
|
||||
"prompt = \"\"\"\n",
|
||||
" a picture of the universe, showing sea of stars, Nebula and Supernova\n",
|
||||
" elegant, detailed digital painting\"\"\"\n",
|
||||
"\n",
|
||||
"results = pipe(prompt=prompt)\n",
|
||||
"images = results.images\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5aed5ed7b6f6"
|
||||
},
|
||||
"source": [
|
||||
"### Text-guided image-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "faadabb7728f"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import (DPMSolverMultistepScheduler,\n",
|
||||
" StableDiffusionImg2ImgPipeline)\n",
|
||||
"\n",
|
||||
"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
|
||||
"model_id = \"stabilityai/stable-diffusion-2-1\"\n",
|
||||
"pipe = StableDiffusionImg2ImgPipeline.from_pretrained(\n",
|
||||
" model_id, torch_dtype=torch.float16\n",
|
||||
")\n",
|
||||
"pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)\n",
|
||||
"pipe = pipe.to(device)\n",
|
||||
"pipe.enable_attention_slicing()\n",
|
||||
"\n",
|
||||
"url = \"https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg\"\n",
|
||||
"\n",
|
||||
"response = requests.get(url)\n",
|
||||
"init_image = Image.open(BytesIO(response.content)).convert(\"RGB\")\n",
|
||||
"init_image = init_image.resize((768, 512))\n",
|
||||
"\n",
|
||||
"prompt = \"A fantasy landscape, trending on artstation\"\n",
|
||||
"\n",
|
||||
"results = pipe(prompt=prompt, image=init_image)\n",
|
||||
"images = results.images\n",
|
||||
"display(init_image)\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune with Dreambooth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0dc65d8f0689"
|
||||
},
|
||||
"source": [
|
||||
"This section uses [dreambooth](https://dreambooth.github.io/) to finetune the [stable-diffusion-2-1](https://huggingface.co/stabilityai/stable-diffusion-2-1) model with [5 dog images](https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ) to personalize the text-to-image model.\n",
|
||||
"\n",
|
||||
"It finetunes both text encoder and unet of the stable diffusion model up to 800 steps. The whole finetuning job takes 45 minutes to finish using 1 A100 GPU.\n",
|
||||
"\n",
|
||||
"The full model will be saved after the finetuning job finishs and it can be loaded by the [StableDiffusionPipeline](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/text2img) to run inference."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "34048707df5c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Download example training images.\n",
|
||||
"!gdown --folder https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ\n",
|
||||
"\n",
|
||||
"# Upload data to Cloud Storage bucket.\n",
|
||||
"upload_local_dir_to_gcs(\"dog\", f\"gs://{GCS_BUCKET}/dreambooth/dog\")\n",
|
||||
"upload_local_dir_to_gcs(\"dog\", f\"gs://{GCS_BUCKET}/dreambooth/dog_class\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "969cfeb79317"
|
||||
},
|
||||
"source": [
|
||||
"**NOTE**: If the upload step fails due to lacking of permission, you need to [grant the Storage Object Admin role](https://cloud.google.com/storage/docs/access-control/using-iam-permissions) for the Cloud account of the notebook."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65467b361315"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-trained model to be loaded.\n",
|
||||
"model_id = \"stabilityai/stable-diffusion-2-1\"\n",
|
||||
"\n",
|
||||
"# Input and output path.\n",
|
||||
"instance_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/dog\"\n",
|
||||
"class_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/dog_class\"\n",
|
||||
"output_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/output\"\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"num_nodes = 1\n",
|
||||
"gpu_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"num_gpus = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = create_job_name(\"dreambooth-stable-diffusion\")\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"# See https://github.com/huggingface/diffusers/blob/v0.14.0/examples/dreambooth/train_dreambooth.py#L75\n",
|
||||
"# for a full list of training arguments.\n",
|
||||
"model = job.run(\n",
|
||||
" args=[\n",
|
||||
" \"dreambooth/train_dreambooth.py\",\n",
|
||||
" f\"--pretrained_model_name_or_path={model_id}\",\n",
|
||||
" \"--train_text_encoder\",\n",
|
||||
" f\"--instance_data_dir={instance_dir}\",\n",
|
||||
" f\"--class_data_dir={class_dir}\",\n",
|
||||
" f\"--output_dir={output_dir}\",\n",
|
||||
" \"--with_prior_preservation\",\n",
|
||||
" \"--prior_loss_weight=1.0\",\n",
|
||||
" \"--instance_prompt='a photo of sks dog'\",\n",
|
||||
" \"--class_prompt='a photo of dog'\",\n",
|
||||
" \"--resolution=512\",\n",
|
||||
" \"--train_batch_size=1\",\n",
|
||||
" \"--gradient_checkpointing\",\n",
|
||||
" \"--learning_rate=2e-6\",\n",
|
||||
" \"--lr_scheduler=constant\",\n",
|
||||
" \"--lr_warmup_steps=0\",\n",
|
||||
" \"--num_class_images=200\",\n",
|
||||
" \"--max_train_steps=800\",\n",
|
||||
" ],\n",
|
||||
" replica_count=num_nodes,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=gpu_type,\n",
|
||||
" accelerator_count=num_gpus,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bf7f82732e61"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and Deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1cc26e68d7b0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cd7b56421392"
|
||||
},
|
||||
"source": [
|
||||
"### Text-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6d331b1ea337"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the stable diffusion model for the text-to-image task.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send a batch of text prompts to the endpoint to generated images.\n",
|
||||
"\n",
|
||||
"When deployed on one V100 GPU, the averaged inference time of a request is ~20 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bf55e38815dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the model_id to \"stabilityai/stable-diffusion-2-1\" to load the OSS pre-trained model.\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=f\"gs://{GCS_BUCKET}/dreambooth/output\", task=\"text-to-image\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus upto 10 minutes of additional waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "4ab04da3ec9a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"a deer in Matisse style\"},\n",
|
||||
" {\"prompt\": \"a sks dog in Van Gogh style\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"image_grid(images)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "af21a3cff1e0"
|
||||
},
|
||||
"source": [
|
||||
"Clean up resources:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c1e51f764a60"
|
||||
},
|
||||
"source": [
|
||||
"### Text-guided image-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fa686a54047c"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the stable diffusion model for the text-guided image-to-image task."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65e32356fbd1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the model_id to a GCS path, like \"gs://GCS_BUCKET/dreambooth/output\", to load the dreambooth finetuned model above.\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"stabilityai/stable-diffusion-2-1\", task=\"image-to-image\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "SsjYFLvNymc0"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "83a50fd4a1ed"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"init_image = download_image(\n",
|
||||
" \"https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg\"\n",
|
||||
")\n",
|
||||
"display(init_image)\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"A fantasy landscape, trending on artstation\",\n",
|
||||
" \"image\": image_to_base64(init_image),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ed3795d474b9"
|
||||
},
|
||||
"source": [
|
||||
"Clean up resources:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b53b883257b4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_stable_diffusion_2_1.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+646
@@ -0,0 +1,646 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1e9c07efb6ac"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Stable Diffusion Inpainting\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.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",
|
||||
" (a Python-3 GPU notebook with preinstalled HuggingFace/transformer libraries is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cd8433ec804a"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates running local inference for [runwayml/stable-diffusion-inpainting](https://huggingface.co/runwayml/stable-diffusion-inpainting) on either [Colab](https://colab.research.google.com) or [Vertex AI Workbench](https://cloud.google.com/vertex-ai-workbench). This notebook also demonstrates finetuning runwayml/stable-diffusion-inpainting with [Dreambooth](https://huggingface.co/docs/diffusers/training/dreambooth) and deploying it on Vertex-AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Run local predictions for image-inpainting with serving dockers.\n",
|
||||
"- Finetune the stable-diffusion-inpainting model with [Dreambooth](https://huggingface.co/docs/diffusers/training/dreambooth).\n",
|
||||
"- Upload the model to [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model to a [Vertex AI Endpoint resource](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image-inpainting.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Gl3bjJsV3k4J"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "CaCslNQE37P_"
|
||||
},
|
||||
"source": [
|
||||
"### Setup notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fb671e75ca7b"
|
||||
},
|
||||
"source": [
|
||||
"#### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dc8ee367fb42"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" ! pip3 install --upgrade pip\n",
|
||||
" ! pip3 install torchvision==0.14.1\n",
|
||||
" ! pip3 install transformers==4.27.1\n",
|
||||
" ! pip3 install diffusers==0.15.1\n",
|
||||
" ! pip3 install datasets==2.9.0\n",
|
||||
" ! pip3 install accelerate==0.18.0\n",
|
||||
" ! pip3 install triton==2.0.0.dev20221120\n",
|
||||
" ! pip3 install xformers==0.0.16\n",
|
||||
" # Install gdown for downloading example training images.\n",
|
||||
" ! pip3 install gdown\n",
|
||||
" # Remove wrong cublas version.\n",
|
||||
" ! pip3 uninstall nvidia_cublas_cu11 --yes\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "JaY__7zz3uWk"
|
||||
},
|
||||
"source": [
|
||||
"#### Workbench only\n",
|
||||
"1. Follow [this link](https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_inpainting.ipynb) to deploy the notebook to a Vertex AI Workbench Instance.\n",
|
||||
"2. Select `Create a new Notebook`.\n",
|
||||
"3. Click `Advanced Options`.\n",
|
||||
"4. Under **Environment**, select `Custom Container` for `Environment`. \n",
|
||||
"5. Set `Docker container image` to `us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/transformers-notebook`.\n",
|
||||
"6. Under **Machine configuration**, select 1 `T4` GPU and select `Install NVIDIA GPU driver automatically for me`.\n",
|
||||
"7. Click `Create` to create the Vertex AI Workbench instance."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n",
|
||||
"\n",
|
||||
"1. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output.\n",
|
||||
"# Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The service account for deploying fine tuned model.\n",
|
||||
"# The service account looks like:\n",
|
||||
"# '<account_name>@<project>.iam.gserviceaccount.com'\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex-AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built training docker image. It contains training scripts and models.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-train:latest\"\n",
|
||||
"\n",
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "8759e624ebc0"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import glob\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform, storage\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"stable-diffusion-inpainting\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
" )\n",
|
||||
" return model, endpoint\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_bucket_and_blob_name(filepath):\n",
|
||||
" # The gcs path is of the form gs://<bucket-name>/<blob-name>\n",
|
||||
" gs_suffix = filepath.split(\"gs://\", 1)[1]\n",
|
||||
" return tuple(gs_suffix.split(\"/\", 1))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_local_dir_to_gcs(local_dir_path, gcs_dir_path):\n",
|
||||
" \"\"\"Uploads files in a local directory to a GCS directory.\"\"\"\n",
|
||||
" client = storage.Client()\n",
|
||||
" bucket_name = gcs_dir_path.split(\"/\")[2]\n",
|
||||
" bucket = client.get_bucket(bucket_name)\n",
|
||||
" for local_file in glob.glob(local_dir_path + \"/**\"):\n",
|
||||
" if not os.path.isfile(local_file):\n",
|
||||
" continue\n",
|
||||
" filename = local_file[1 + len(local_dir_path) :]\n",
|
||||
" gcs_file_path = os.path.join(gcs_dir_path, filename)\n",
|
||||
" _, blob_name = get_bucket_and_blob_name(gcs_file_path)\n",
|
||||
" blob = bucket.blob(blob_name)\n",
|
||||
" blob.upload_from_filename(local_file)\n",
|
||||
" print(\"Copied {} to {}.\".format(local_file, gcs_file_path))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Wx18bDXC2XPR"
|
||||
},
|
||||
"source": [
|
||||
"## Run inferences locally"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "OSYVyAvu2mfN"
|
||||
},
|
||||
"source": [
|
||||
"### Image-inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "CZ2RkKT32Zj2"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionInpaintPipeline\n",
|
||||
"\n",
|
||||
"image_url = \"https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png\"\n",
|
||||
"image_response = requests.get(image_url)\n",
|
||||
"init_image = Image.open(BytesIO(image_response.content)).convert(\"RGB\")\n",
|
||||
"display(init_image)\n",
|
||||
"\n",
|
||||
"mask_url = \"https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png\"\n",
|
||||
"mask_response = requests.get(mask_url)\n",
|
||||
"mask_image = Image.open(BytesIO(mask_response.content)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"pipe = StableDiffusionInpaintPipeline.from_pretrained(\n",
|
||||
" \"runwayml/stable-diffusion-inpainting\",\n",
|
||||
" revision=\"fp16\",\n",
|
||||
" torch_dtype=torch.float16,\n",
|
||||
")\n",
|
||||
"pipe.to(\"cuda\")\n",
|
||||
"\n",
|
||||
"prompt = \"Face of a yellow cat, high resolution, sitting on a park bench\"\n",
|
||||
"images = pipe(prompt=prompt, image=init_image, mask_image=mask_image).images\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e70e3519ff8b"
|
||||
},
|
||||
"source": [
|
||||
"## Finetune with Dreambooth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f560edbf96c6"
|
||||
},
|
||||
"source": [
|
||||
"This section uses [dreambooth](https://dreambooth.github.io/) to finetune the [stable-diffusion-inpainting](https://huggingface.co/runwayml/stable-diffusion-inpainting) model with [5 dog images](https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ) to personalize the model.\n",
|
||||
"\n",
|
||||
"It finetunes both text encoder and unet of the stable diffusion model up to 800 steps. The whole finetuning job takes 30 minutes to finish using 1 A100 GPU.\n",
|
||||
"\n",
|
||||
"The full model will be saved after the finetuning job finishs and it can be loaded by the [StableDiffusionInpaintPipeline](https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/inpaint) to run inference."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "34048707df5c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Download example training images.\n",
|
||||
"!gdown --folder https://drive.google.com/drive/folders/1BO_dyz-p65qhBRRMRA4TbZ8qW4rB99JZ\n",
|
||||
"\n",
|
||||
"# Upload data to Cloud Storage bucket.\n",
|
||||
"upload_local_dir_to_gcs(\"dog\", f\"gs://{GCS_BUCKET}/dreambooth/dog\")\n",
|
||||
"upload_local_dir_to_gcs(\"dog\", f\"gs://{GCS_BUCKET}/dreambooth/dog_class\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "f6d5a05592e1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-trained model to be loaded.\n",
|
||||
"model_id = \"runwayml/stable-diffusion-inpainting\"\n",
|
||||
"\n",
|
||||
"# Input and output path.\n",
|
||||
"instance_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/dog\"\n",
|
||||
"class_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/dog_class\"\n",
|
||||
"output_dir = f\"/gcs/{GCS_BUCKET}/dreambooth/output\"\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"machine_type = \"a2-highgpu-1g\"\n",
|
||||
"num_nodes = 1\n",
|
||||
"gpu_type = \"NVIDIA_TESLA_A100\"\n",
|
||||
"num_gpus = 1\n",
|
||||
"\n",
|
||||
"# Setup training job.\n",
|
||||
"job_name = create_job_name(\"dreambooth-stable-diffusion-inpainting\")\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Pass training arguments and launch job.\n",
|
||||
"# See https://github.com/huggingface/diffusers/blob/v0.14.0/examples/research_projects/dreambooth_inpaint/train_dreambooth_inpaint.py#L83\n",
|
||||
"# for a full list of training arguments.\n",
|
||||
"model = job.run(\n",
|
||||
" args=[\n",
|
||||
" \"research_projects/dreambooth_inpaint/train_dreambooth_inpaint.py\",\n",
|
||||
" f\"--pretrained_model_name_or_path={model_id}\",\n",
|
||||
" \"--train_text_encoder\",\n",
|
||||
" f\"--instance_data_dir={instance_dir}\",\n",
|
||||
" f\"--class_data_dir={class_dir}\",\n",
|
||||
" f\"--output_dir={output_dir}\",\n",
|
||||
" \"--with_prior_preservation\",\n",
|
||||
" \"--prior_loss_weight=1.0\",\n",
|
||||
" \"--instance_prompt='a photo of sks dog'\",\n",
|
||||
" \"--class_prompt='a photo of dog'\",\n",
|
||||
" \"--resolution=512\",\n",
|
||||
" \"--train_batch_size=1\",\n",
|
||||
" \"--gradient_checkpointing\",\n",
|
||||
" \"--learning_rate=2e-6\",\n",
|
||||
" \"--lr_scheduler=constant\",\n",
|
||||
" \"--lr_warmup_steps=0\",\n",
|
||||
" \"--num_class_images=200\",\n",
|
||||
" \"--max_train_steps=800\",\n",
|
||||
" ],\n",
|
||||
" replica_count=num_nodes,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=gpu_type,\n",
|
||||
" accelerator_count=num_gpus,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "90d3c379090e"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1cc26e68d7b0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "b8bb7d198315"
|
||||
},
|
||||
"source": [
|
||||
"### Image-inpainting"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "79b66382f849"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the stable diffusion model for the image-inpainting task.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send prompts to the endpoint to generated images.\n",
|
||||
"\n",
|
||||
"When deployed on one V100 GPU, the averaged inference time of a request is ~15 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "a881564da1d8"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set the model_id to \"runwayml/stable-diffusion-inpainting\" to load the OSS pre-trained model.\n",
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=f\"gs://{GCS_BUCKET}/dreambooth/output\", task=\"image-inpainting\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ca1761afb66f"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"img_url = \"https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/bertrand-gabioud-CpuFzIsHYJ0.png\"\n",
|
||||
"mask_url = \"https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/bertrand-gabioud-CpuFzIsHYJ0_mask.png\"\n",
|
||||
"init_image = download_image(img_url).resize((512, 512))\n",
|
||||
"mask_image = download_image(mask_url).resize((512, 512))\n",
|
||||
"display(init_image)\n",
|
||||
"display(mask_image)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"a tree, high resolution, in front of high buildings\",\n",
|
||||
" \"image\": image_to_base64(init_image),\n",
|
||||
" \"mask_image\": image_to_base64(mask_image),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"display(images[0])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "f12f8d9c2786"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "911406c1561e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_stable_diffusion_inpainting.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Stable Diffusion Upscaler\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_upscaler.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_upscaler.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_upscaler.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",
|
||||
" (a Python-3 GPU notebook with preinstalled HuggingFace/transformer libraries is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates running local inference for [stabilityai/stable-diffusion-x4-upscaler](https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler) on either [Colab](https://colab.research.google.com) or [Vertex AI Workbench](https://cloud.google.com/vertex-ai-workbench). This notebook also demonstrates deploying the model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Run local predictions for text-guided image super resolution.\n",
|
||||
"- Upload the model to [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model to a [Vertex AI Endpoint resource](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for text-guided image super resolution.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Setup notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"#### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
" ! pip3 install --upgrade pip\n",
|
||||
" ! pip3 install torchvision==0.14.1\n",
|
||||
" ! pip3 install transformers==4.27.1\n",
|
||||
" ! pip3 install diffusers==0.15.1\n",
|
||||
" ! pip3 install datasets==2.9.0\n",
|
||||
" ! pip3 install accelerate==0.18.0\n",
|
||||
" ! pip3 install triton==2.0.0.dev20221120\n",
|
||||
" ! pip3 install xformers==0.0.16\n",
|
||||
" # Remove wrong cublas version.\n",
|
||||
" ! pip3 uninstall nvidia_cublas_cu11 --yes\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fb671e75ca7b"
|
||||
},
|
||||
"source": [
|
||||
"#### Workbench only\n",
|
||||
"1. Follow [this link](https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_stable_diffusion_upscaler.ipynb) to deploy the notebook to a Vertex AI Workbench Instance.\n",
|
||||
"2. Select `Create a new Notebook`.\n",
|
||||
"3. Click `Advanced Options`, and select `Debian 10` for `Operating System`.\n",
|
||||
"4. Under **Environment**, select `Custom Container` for `Environment`.\n",
|
||||
"5. Set `Docker container image` to `us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/transformers-notebook`.\n",
|
||||
"6. Under **Machine configuration**, select 1 `T4` GPU and select `Install NVIDIA GPU driver automatically for me`.\n",
|
||||
"7. Click `Create` to create the Vertex AI Workbench instance."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex-AI API\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Expand an image to a square image.\n",
|
||||
"def expand2square(pil_img, background_color):\n",
|
||||
" width, height = pil_img.size\n",
|
||||
" if width == height:\n",
|
||||
" return pil_img\n",
|
||||
" elif width > height:\n",
|
||||
" result = Image.new(pil_img.mode, (width, width), background_color)\n",
|
||||
" result.paste(pil_img, (0, (width - height) // 2))\n",
|
||||
" return result\n",
|
||||
" else:\n",
|
||||
" result = Image.new(pil_img.mode, (height, height), background_color)\n",
|
||||
" result.paste(pil_img, ((height - width) // 2, 0))\n",
|
||||
" return result\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Resize an image to a square image.\n",
|
||||
"def resize2square(pil_img, target_size, background_color):\n",
|
||||
" low_res_size = target_size\n",
|
||||
" wpercent = low_res_size / float(pil_img.size[0])\n",
|
||||
" hsize = int(float(pil_img.size[1]) * float(wpercent))\n",
|
||||
" result_img = pil_img.resize((low_res_size, hsize), Image.ANTIALIAS)\n",
|
||||
" return expand2square(result_img, background_color)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=\"JPEG\")\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Deploy a model to Vertex AI endpoint.\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"stable-diffusion-upscaler\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-{task}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e8a42fa49305"
|
||||
},
|
||||
"source": [
|
||||
"## Run inferences locally\n",
|
||||
"\n",
|
||||
"The code below downloads the model and runs the model locally with a low resolution image."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "faadabb7728f"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import torch\n",
|
||||
"from diffusers import StableDiffusionUpscalePipeline\n",
|
||||
"from IPython import display\n",
|
||||
"\n",
|
||||
"device = \"cuda\" if torch.cuda.is_available() else \"cpu\"\n",
|
||||
"model_id = \"stabilityai/stable-diffusion-x4-upscaler\"\n",
|
||||
"pipeline = StableDiffusionUpscalePipeline.from_pretrained(\n",
|
||||
" model_id, torch_dtype=torch.float16\n",
|
||||
")\n",
|
||||
"pipeline = pipeline.to(device)\n",
|
||||
"\n",
|
||||
"prompt = \"Roaring tiger, close shot with dark circular pupils, DSLR\"\n",
|
||||
"url = \"https://as2.ftcdn.net/v2/jpg/02/11/74/81/1000_F_211748113_fUCaGzZIdMk5JaehpyVttwVMWbPWUtBz.jpg\"\n",
|
||||
"low_res_img = download_image(url)\n",
|
||||
"low_res_img = resize2square(low_res_img, 256, (0, 0, 0))\n",
|
||||
"print(\"Low resolution input image:\")\n",
|
||||
"display.display(low_res_img)\n",
|
||||
"\n",
|
||||
"pipeline.enable_xformers_memory_efficient_attention()\n",
|
||||
"upscaled_image = pipeline(\n",
|
||||
" prompt=prompt, image=low_res_img, num_inference_steps=20\n",
|
||||
").images[0]\n",
|
||||
"print(\"Upscaled high resolution image:\")\n",
|
||||
"display.display(upscaled_image)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bf7f82732e61"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and Deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1cc26e68d7b0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the stable diffusion upscaler model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65e32356fbd1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"stabilityai/stable-diffusion-x4-upscaler\", task=\"conditioned-super-res\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "SsjYFLvNymc0"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "83a50fd4a1ed"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from IPython import display\n",
|
||||
"\n",
|
||||
"prompt = \"Roaring tiger, close shot with dark circular pupils, DSLR\"\n",
|
||||
"url = \"https://as2.ftcdn.net/v2/jpg/02/11/74/81/1000_F_211748113_fUCaGzZIdMk5JaehpyVttwVMWbPWUtBz.jpg\"\n",
|
||||
"low_res_img = download_image(url)\n",
|
||||
"low_res_size = 256\n",
|
||||
"low_res_img = resize2square(low_res_img, low_res_size, (0, 0, 0))\n",
|
||||
"print(\"Low resolution input image:\")\n",
|
||||
"display.display(low_res_img)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": prompt,\n",
|
||||
" \"image\": image_to_base64(low_res_img),\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"images = [base64_to_image(image) for image in response.predictions]\n",
|
||||
"print(\"Upscaled high resolution image:\")\n",
|
||||
"display.display(upscaled_image)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ed3795d474b9"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b53b883257b4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_stable_diffusion_upscaler.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,419 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ug_ZXeBdbFI4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Jr2jRuqabG1m"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Text To Video\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_text_to_video.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_text_to_video.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_text_to_video.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "wLLfRT_6bTZO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [Text To Video](https://huggingface.co/docs/diffusers/main/en/api/pipelines/text_to_video) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for text-to-video.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "J_fFK5ufbil8"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\n",
|
||||
"\n",
|
||||
"**NOTE**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dkbL0d3lbnI9"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only\n",
|
||||
"\n",
|
||||
"Run the following commands for colab and skip this section if you use workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "fsQlA1Yibroo"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KsbbsTU5bwtJ"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "izM_LAUwcHSa"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "okVArp5bb3Wu"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"text-to-video\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "c277da31bde6"
|
||||
},
|
||||
"source": [
|
||||
"### Text-guided image-to-image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "a5a86996222c"
|
||||
},
|
||||
"source": [
|
||||
"Deploy the model for the text-to-video task.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send prompts to the endpoint to generated images.\n",
|
||||
"\n",
|
||||
"When deployed on one V100 GPU, the averaged inference time of a request is ~30 seconds."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"damo-vilab/text-to-video-ms-1.7b\", task=\"text-to-video\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"instances = [\n",
|
||||
" {\"prompt\": \"Spiderman is surfing\"},\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0XeaCIfOjjOP"
|
||||
},
|
||||
"source": [
|
||||
"You can view the generated video directly by executing the following cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "nriQMwuOiS0k"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from IPython.display import HTML\n",
|
||||
"\n",
|
||||
"html = \"\"\n",
|
||||
"for video in response.predictions:\n",
|
||||
" html += \"<video controls>\"\n",
|
||||
" html += f'<source src=\"data:video/mp4;base64,{video}\" type=\"video/mp4\">'\n",
|
||||
" html += \"</video>\"\n",
|
||||
"HTML(html)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_text_to_video.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "99c1c3fc2ca5"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - Zero-Shot Text-to-Video Generation\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_text_to_video_zero_shot.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_text_to_video_zero_shot.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_text_to_video_zero_shot.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",
|
||||
" (a Python-3 GPU notebook with preinstalled HuggingFace/transformer libraries is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3de7470326a2"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying model on Vertex AI and running online inference for [Zero-Shot Text-to-Video Generation](https://huggingface.co/docs/diffusers/api/pipelines/text_to_video_zero).\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Vertex AI Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model to a [Vertex AI Endpoint resource](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for zero-shot text-to-video generation.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\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": "ioensNKM8ned"
|
||||
},
|
||||
"source": [
|
||||
"### Setup notebook"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"#### Colab only\n",
|
||||
"Run the following commands for Colab and skip this section if you are using Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bb7adab99e41"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6c460088b873"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "855d6b96f291"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "e828eb320337"
|
||||
},
|
||||
"source": [
|
||||
"### Initialize Vertex-AI API\n",
|
||||
"\n",
|
||||
"Initialize the Vertex AI SDK for Python for your project and corresponding bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "12cd25839741"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2cc825514deb"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b42bd4fa2b2d"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-diffusers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0c250872074f"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "354da31189dc"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Deploy a model to Vertex AI endpoint.\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"stable-diffusion-zero-shot-text-to-video\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-{task}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/diffusers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_V100\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bf7f82732e61"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and Deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "1cc26e68d7b0"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the zero-shot text-to-video model to Model Registry and deploys it on the Endpoint.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "65e32356fbd1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"runwayml/stable-diffusion-v1-5\", task=\"text-to-video-zero-shot\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "SsjYFLvNymc0"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "83a50fd4a1ed"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from IPython.display import HTML\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"a rainbow butterfly in a garden\",\n",
|
||||
" },\n",
|
||||
" {\n",
|
||||
" \"prompt\": \"a spaceship flying through solar system\",\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"response = endpoint.predict(instances=instances)\n",
|
||||
"html = \"\"\n",
|
||||
"for i, video in enumerate(response.predictions):\n",
|
||||
" html += (\n",
|
||||
" '<iframe width=\"480\" height=\"480\" src=\"'\n",
|
||||
" + \"data:video/mp4;base64,\"\n",
|
||||
" + video\n",
|
||||
" + '\" frameborder=\"2\" allowfullscreen></iframe>'\n",
|
||||
" )\n",
|
||||
"HTML(\n",
|
||||
" html\n",
|
||||
" + '<iframe width=\"0\" height=\"480\" src=\"\" frameborder=\"0\" allowfullscreen></iframe>'\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ed3795d474b9"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b53b883257b4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_text_to_video_zero_shot.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,755 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ApNHZJmT2AMH"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "YfxrFG052AMI"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - TIMM\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_timm.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_timm.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_timm.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "76BCoQcm2AMJ"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates running local inference using the [timm](https://github.com/rwightman/pytorch-image-models) library, finetuning the PyTorch [timm models](https://github.com/huggingface/pytorch-image-models#models), and deploying the models on [Vertex AI](https://cloud.google.com/vertex-ai).\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Setup environment.\n",
|
||||
"- Run inference locally using the timm library.\n",
|
||||
"- Create a custom training job on Vertex AI to train or finetune a model.\n",
|
||||
"- Deploy the model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9iU1NKfh2AMJ"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "7l3GN-QL2AMJ"
|
||||
},
|
||||
"source": [
|
||||
"### Setup cloud project\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). Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage.\n",
|
||||
"\n",
|
||||
"1. [Enable Artifact Registry](https://cloud.google.com/artifact-registry/docs/enable-service) and [create a repository](https://cloud.google.com/artifact-registry/docs/repositories/create-repos) for storing docker images.\n",
|
||||
"\n",
|
||||
"1. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs.\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. [Create a service account](https://cloud.google.com/iam/docs/service-accounts-create#iam-service-accounts-create-console) with `Vertex AI User` and `Storage Object Admin` roles for deploying fine tuned model to Vertex AI endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "hyAQXmgf2AMK"
|
||||
},
|
||||
"source": [
|
||||
"### Setup required libraries\n",
|
||||
"\n",
|
||||
"It's highly recommended to run this notebook on [Vertex AI workbench](https://cloud.google.com/vertex-ai-workbench), where you don't need to manually install any additional libraries.\n",
|
||||
"\n",
|
||||
"If you are running this notebook locally, you will need to install the [Cloud SDK](https://cloud.google.com/sdk) and [gsutil](https://cloud.google.com/storage/docs/gsutil_install)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ad1f36f4b5c3"
|
||||
},
|
||||
"source": [
|
||||
"### Install libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cb041d87f50a"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip3 install timm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "-uF7Kb112AMK"
|
||||
},
|
||||
"source": [
|
||||
"### Colab Only\n",
|
||||
"Run the following commands for colab and skip this section if you use workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2lP6dnfy2AMK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fo65Wg9Y2AMK"
|
||||
},
|
||||
"source": [
|
||||
"### Setup environment variables\n",
|
||||
"\n",
|
||||
"This notebook supports models in https://huggingface.co/docs/timm/models.\n",
|
||||
"\n",
|
||||
"You can also run\n",
|
||||
"`python -c \"from timm import list_models; print(list_models(pretrained=True))\"`\n",
|
||||
"locally to see all pretrained models.\n",
|
||||
"\n",
|
||||
"The following models have been manually verified to work with this notebook:\n",
|
||||
"\n",
|
||||
"* vit_tiny_patch16_224\n",
|
||||
"* beit_base_patch16_224\n",
|
||||
"* deit3_small_patch16_224\n",
|
||||
"* efficientnet_b2\n",
|
||||
"* mobilenetv2_100\n",
|
||||
"* resnet50\n",
|
||||
"* resnest50d\n",
|
||||
"* convnext_base\n",
|
||||
"* cspdarknet53\n",
|
||||
"* inception_v4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "3msH2i5V2AMK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"# The region for running jobs.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The model you want to train and serve. Please select a model from the verified model list above.\n",
|
||||
"# We use a ViT model as the example.\n",
|
||||
"MODEL_NAME = \"vit_tiny_patch16_224\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket name without gs:// prefix for training outputs.\n",
|
||||
"# For example: test_bucket\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The service account for deploying fine tuned model. It looks like:\n",
|
||||
"# '<account_name>@<project>.iam.gserviceaccount.com'\n",
|
||||
"# Follow step 6 above to create this account.\n",
|
||||
"SERVICE_ACCOUNT = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "14a500024b7b"
|
||||
},
|
||||
"source": [
|
||||
"## Run local inference\n",
|
||||
"\n",
|
||||
"This section runs local inference on an image using the model chosen in above section."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "54859170c1ad"
|
||||
},
|
||||
"source": [
|
||||
"### Import libraries\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7505a2bf3897"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import urllib\n",
|
||||
"\n",
|
||||
"import timm\n",
|
||||
"import torch\n",
|
||||
"from PIL import Image\n",
|
||||
"from timm.data import resolve_data_config\n",
|
||||
"from timm.data.transforms_factory import create_transform"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "17e6d8a0dac3"
|
||||
},
|
||||
"source": [
|
||||
"### Load a pretrained model"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "8b6bbe928998"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = timm.create_model(MODEL_NAME, pretrained=True)\n",
|
||||
"model.eval()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f2ce8fa5439"
|
||||
},
|
||||
"source": [
|
||||
"### Load and preprocess the image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6390302c9761"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"config = resolve_data_config({}, model=model)\n",
|
||||
"transform = create_transform(**config)\n",
|
||||
"\n",
|
||||
"# The example downloads a test image. You can upload and use your own images\n",
|
||||
"# by changing IMAGE_FILENAME.\n",
|
||||
"! wget https://github.com/pytorch/hub/raw/master/images/dog.jpg -O test.jpg\n",
|
||||
"IMAGE_FILENAME = \"test.jpg\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can also copy over images stored in a GCS bucket with the line below.\n",
|
||||
"# ! gsutil cp \"gs://path/to/image\" \"test.jpg\"\n",
|
||||
"\n",
|
||||
"img = Image.open(IMAGE_FILENAME).convert(\"RGB\")\n",
|
||||
"tensor = transform(img).unsqueeze(0) # transform and add batch dimension\n",
|
||||
"display(img)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8a6e2ea460ad"
|
||||
},
|
||||
"source": [
|
||||
"### Get the model predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "0a7a666a0aef"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"with torch.no_grad():\n",
|
||||
" out = model(tensor)\n",
|
||||
"probabilities = torch.nn.functional.softmax(out[0], dim=0)\n",
|
||||
"print(probabilities.shape)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "691139fc5789"
|
||||
},
|
||||
"source": [
|
||||
"### Get the top-5 predictions class names"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "147249af5dec"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Get imagenet class mappings\n",
|
||||
"url, filename = (\n",
|
||||
" \"https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt\",\n",
|
||||
" \"imagenet_classes.txt\",\n",
|
||||
")\n",
|
||||
"urllib.request.urlretrieve(url, filename)\n",
|
||||
"with open(\"imagenet_classes.txt\") as f:\n",
|
||||
" categories = [s.strip() for s in f.readlines()]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "a8740b668da6"
|
||||
},
|
||||
"source": [
|
||||
"### Print top categories per image"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9f18bcd806f2"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"top5_prob, top5_catid = torch.topk(probabilities, 5)\n",
|
||||
"for i in range(top5_prob.size(0)):\n",
|
||||
" print(categories[top5_catid[i]], top5_prob[i].item())\n",
|
||||
"# prints class names and probabilities like:\n",
|
||||
"# [('Samoyed', 0.6425196528434753), ('Pomeranian', 0.04062102362513542), ('keeshond', 0.03186424449086189), ('white wolf', 0.01739676296710968), ('Eskimo dog', 0.011717947199940681)]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "4120d1585355"
|
||||
},
|
||||
"source": [
|
||||
"## Run training jobs\n",
|
||||
"\n",
|
||||
"This section runs a regular training job or a hyperparameter tuning job on Vertex AI.\n",
|
||||
"\n",
|
||||
"Before creating a training job, you need to prepare the dataset for training and evaluation.\n",
|
||||
"\n",
|
||||
"For example, you can use [ImageNet-1K](https://huggingface.co/datasets/imagenet-1k) held on a Cloud Storage bucket as the input dataset."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "qy4gKksX2AMK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The prebuilt training docker uri.\n",
|
||||
"TRAIN_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-timm-train\"\n",
|
||||
"\n",
|
||||
"# The path to data directory on Cloud Storage without gs:// prefix.\n",
|
||||
"# In the form of: <bucket-name>/path-to-data\n",
|
||||
"GCS_DATA_DIR = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0DnlJhO72AMK"
|
||||
},
|
||||
"source": [
|
||||
"### Create a training job on Vertex AI\n",
|
||||
"\n",
|
||||
"This section creates a training job on Vertex AI. If you want to create a hyperparameter tuning job instead, you can skip to the next section."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1R5JWJGS2AML"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# Init common setup.\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)\n",
|
||||
"\n",
|
||||
"# Input and output path.\n",
|
||||
"data_dir = f\"/gcs/{GCS_DATA_DIR}\"\n",
|
||||
"output_dir = f\"/gcs/{GCS_BUCKET}/timm\"\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"# Single node with multiple GPUs.\n",
|
||||
"machine_type = \"n1-highmem-32\"\n",
|
||||
"num_nodes = 1\n",
|
||||
"gpu_type = \"NVIDIA_TESLA_P100\" # @param {type:\"string\"}\n",
|
||||
"num_gpus = 4 # @param {type:\"integer\"}\n",
|
||||
"\n",
|
||||
"# Model specific config.\n",
|
||||
"job_name = f\"pytorch-{MODEL_NAME}\"\n",
|
||||
"batch_size = 32\n",
|
||||
"epochs = 2\n",
|
||||
"\n",
|
||||
"job = aiplatform.CustomContainerTrainingJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" container_uri=TRAIN_DOCKER_URI,\n",
|
||||
")\n",
|
||||
"model = job.run(\n",
|
||||
" args=[\n",
|
||||
" \"--standalone\",\n",
|
||||
" f\"--nnodes={num_nodes}\",\n",
|
||||
" f\"--nproc_per_node={num_gpus}\",\n",
|
||||
" \"train.py\",\n",
|
||||
" data_dir,\n",
|
||||
" f\"--model={MODEL_NAME}\",\n",
|
||||
" \"--pretrained\",\n",
|
||||
" f\"--output={output_dir}\",\n",
|
||||
" f\"--batch-size={batch_size}\",\n",
|
||||
" f\"--epochs={epochs}\",\n",
|
||||
" ],\n",
|
||||
" replica_count=num_nodes,\n",
|
||||
" machine_type=machine_type,\n",
|
||||
" accelerator_type=gpu_type,\n",
|
||||
" accelerator_count=num_gpus,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "Tf6P7ZI82AML"
|
||||
},
|
||||
"source": [
|
||||
"### Create a hyperparameter tuning job on Vertex AI\n",
|
||||
"\n",
|
||||
"You can use a [hyperparameter tuning](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) job to find the best configuration of your hyperparameters.\n",
|
||||
"\n",
|
||||
"You can skip this section if you already trained a model in the previous section and do not want to tune the hyperparameters."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Hy_aCff_2AML"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
|
||||
"\n",
|
||||
"# Init common setup.\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)\n",
|
||||
"\n",
|
||||
"# Input and output path.\n",
|
||||
"data_dir = f\"/gcs/{GCS_DATA_DIR}\"\n",
|
||||
"output_dir = f\"/gcs/{GCS_BUCKET}/timm\"\n",
|
||||
"\n",
|
||||
"# Model specific config.\n",
|
||||
"job_name = f\"pytorch-hp-{MODEL_NAME}\"\n",
|
||||
"batch_size = 32\n",
|
||||
"epochs = 2\n",
|
||||
"\n",
|
||||
"# Worker pool spec.\n",
|
||||
"machine_type = \"n1-highmem-16\"\n",
|
||||
"num_nodes = 1\n",
|
||||
"gpu_type = \"NVIDIA_TESLA_V100\" # @param {type:\"string\"}\n",
|
||||
"num_gpus = 2 # @param {type:\"integer\"}\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": machine_type,\n",
|
||||
" \"accelerator_type\": gpu_type,\n",
|
||||
" \"accelerator_count\": num_gpus,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": num_nodes,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAIN_DOCKER_URI,\n",
|
||||
" \"args\": [\n",
|
||||
" \"--standalone\",\n",
|
||||
" f\"--nnodes={num_nodes}\",\n",
|
||||
" f\"--nproc_per_node={num_gpus}\",\n",
|
||||
" \"train.py\",\n",
|
||||
" data_dir,\n",
|
||||
" f\"--model={MODEL_NAME}\",\n",
|
||||
" \"--pretrained\",\n",
|
||||
" f\"--output={output_dir}\",\n",
|
||||
" f\"--batch-size={batch_size}\",\n",
|
||||
" f\"--epochs={epochs}\",\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"# Hyperparameter job specs.\n",
|
||||
"metric_spec = {\"top1_accuracy\": \"maximize\"}\n",
|
||||
"parameter_spec = {\n",
|
||||
" \"lr\": hpt.DoubleParameterSpec(min=0.001, max=0.05, scale=\"log\"),\n",
|
||||
"}\n",
|
||||
"max_trial_count = 2\n",
|
||||
"parallel_trial_count = 2\n",
|
||||
"\n",
|
||||
"# Launch jobs.\n",
|
||||
"training_job = aiplatform.CustomJob(\n",
|
||||
" display_name=job_name, worker_pool_specs=worker_pool_specs\n",
|
||||
")\n",
|
||||
"hp_job = aiplatform.HyperparameterTuningJob(\n",
|
||||
" display_name=job_name,\n",
|
||||
" custom_job=training_job,\n",
|
||||
" metric_spec=metric_spec,\n",
|
||||
" parameter_spec=parameter_spec,\n",
|
||||
" max_trial_count=max_trial_count,\n",
|
||||
" parallel_trial_count=parallel_trial_count,\n",
|
||||
")\n",
|
||||
"hp_job.run()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "DAyRWwqW2AML"
|
||||
},
|
||||
"source": [
|
||||
"## Deploy model for online prediction\n",
|
||||
"\n",
|
||||
"This section uploads the model to Model Registry and deploys it on an Endpoint resource.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jbNbg0yR2AML"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The prebuilt serving docker uri.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/pytorch-timm-serve\"\n",
|
||||
"# The port number used by torchserve traffic.\n",
|
||||
"SERVE_PORT = 7080\n",
|
||||
"# The path to model checkpoint file, including gs:// prefix.\n",
|
||||
"MODEL_PT_PATH = \"gs://path_to_model_best.pth.tar\" # @param {type:\"string\"}\n",
|
||||
"# [Optional] the path to index_to_name.json, including gs:// prefix.\n",
|
||||
"INDEX_TO_NAME_FILE = \"gs://path_to_index_to_name.json\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "INPri3HQ2AML"
|
||||
},
|
||||
"source": [
|
||||
"### Upload and deploy model on Vertex AI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "WOvqoAaN2AML"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Upload model.\n",
|
||||
"serving_env = {\n",
|
||||
" \"MODEL_NAME\": MODEL_NAME,\n",
|
||||
" \"MODEL_PT_PATH\": MODEL_PT_PATH,\n",
|
||||
" \"INDEX_TO_NAME_FILE\": INDEX_TO_NAME_FILE,\n",
|
||||
"}\n",
|
||||
"model = aiplatform.Model.upload(\n",
|
||||
" display_name=MODEL_NAME,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[SERVE_PORT],\n",
|
||||
" serving_container_predict_route=\"/predictions/timm_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
")\n",
|
||||
"# Or reuse a pre-uploaded model.\n",
|
||||
"# model = aiplatform.Model('projects/123456789/locations/us-central1/models/123456789@1')\n",
|
||||
"\n",
|
||||
"# Create an endpoint.\n",
|
||||
"endpoint = aiplatform.Endpoint.create(display_name=\"pytorch-timm-endpoint\")\n",
|
||||
"# Or reuse a pre-created endpoint.\n",
|
||||
"# endpoint = aiplatform.Endpoint('projects/123456789/locations/us-central1/endpoints/123456789')\n",
|
||||
"\n",
|
||||
"# Deploy model to endpoint.\n",
|
||||
"model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" traffic_percentage=100,\n",
|
||||
" service_account=SERVICE_ACCOUNT,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "s2gnYFEJ2AML"
|
||||
},
|
||||
"source": [
|
||||
"You can mange your uploaded models in the [Model Registry](https://pantheon.corp.google.com/vertex-ai/models) and your endpoints in the [Endpoints](https://pantheon.corp.google.com/vertex-ai/endpoints)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5UP84Q5R2AMM"
|
||||
},
|
||||
"source": [
|
||||
"### Test online prediction\n",
|
||||
"\n",
|
||||
"You will now test the deployed endpoint. Please prepare an image to predict."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "zL2Qbm2x2AMM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"\n",
|
||||
"# You can get the deployed endpoint object by its resource name returned by Endpoint.create(). For example:\n",
|
||||
"# endpoint = aiplatform.Endpoint('projects/816369962409/locations/us-central1/endpoints/8809168414485512192')\n",
|
||||
"\n",
|
||||
"# Please upload an image and enter its filename below.\n",
|
||||
"IMAGE_FILENAME = \"test.jpg\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# Alternatively, uncomment the following line to download a cat image for demonstration.\n",
|
||||
"# ! wget http://images.cocodataset.org/val2017/000000039769.jpg -O test.jpg\n",
|
||||
"\n",
|
||||
"with open(IMAGE_FILENAME, \"rb\") as f:\n",
|
||||
" image_b64 = base64.b64encode(f.read()).decode(\"utf-8\")\n",
|
||||
"instances = [{\"data\": {\"b64\": image_b64}}]\n",
|
||||
"\n",
|
||||
"prediction = endpoint.predict(instances=instances)\n",
|
||||
"print(prediction)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KqSxSyT42AMM"
|
||||
},
|
||||
"source": [
|
||||
"### Clean Up Resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "tMwdxiH-2AMM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"endpoint.undeploy_all()\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_timm.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,398 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - ViLT VQA\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_vilt_vqa.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_vilt_vqa.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_vilt_vqa.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [ViLT VQA](https://huggingface.co/dandelin/vilt-b32-finetuned-vqa) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"vilt-vqa\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send images and questions to get answers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"dandelin/vilt-b32-finetuned-vqa\", task=\"visual-question-answering\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"question = \"Which cat is bigger?\"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image), \"text\": question},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(question)\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_vilt_vqa.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "7d9bbf86da5e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "2bd716bf3e39"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden - ViT-GPT2 Image Captioning\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_vit_gpt2_image_captioning.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_pytorch_vit_gpt2_image_captioning.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_pytorch_vit_gpt2_image_captioning.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",
|
||||
" (a Python-3 CPU notebook is recommended)\n",
|
||||
" </td>\n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d8cd12648da4"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates deploying the pre-trained [ViT-GPT2 Image Captioning](https://huggingface.co/nlpconnect/vit-gpt2-image-captioning) model on Vertex AI for online prediction.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"- Upload the model to [Model Registry](https://cloud.google.com/vertex-ai/docs/model-registry/introduction).\n",
|
||||
"- Deploy the model on [Endpoint](https://cloud.google.com/vertex-ai/docs/predictions/using-private-endpoints).\n",
|
||||
"- Run online predictions for image captioning.\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage pricing](https://cloud.google.com/storage/pricing), and use the [Pricing Calculator](https://cloud.google.com/products/calculator/) to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "264c07757582"
|
||||
},
|
||||
"source": [
|
||||
"## Setup environment\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": "d73ffa0c0b83"
|
||||
},
|
||||
"source": [
|
||||
"### Colab only"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2707b02ef5df"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # Restart the notebook kernel after installs.\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0f826ff482a2"
|
||||
},
|
||||
"source": [
|
||||
"### Setup Google Cloud project\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. [Create a Cloud Storage bucket](https://cloud.google.com/storage/docs/creating-buckets) for storing experiment outputs."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8958ebc71868"
|
||||
},
|
||||
"source": [
|
||||
"Fill following variables for experiments environment:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "9db30f827a65"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Cloud project id.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The region you want to launch jobs in.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# The Cloud Storage bucket for storing experiments output. Fill it without the 'gs://' prefix.\n",
|
||||
"GCS_BUCKET = \"\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "92f16e22c20b"
|
||||
},
|
||||
"source": [
|
||||
"Initialize Vertex AI API:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "1680c257acfb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=GCS_BUCKET)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6ca48b699d17"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "de9882ea89ea"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# The pre-built serving docker image. It contains serving scripts and models.\n",
|
||||
"SERVE_DOCKER_URI = \"us-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-transformers-serve\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "10188266a5cd"
|
||||
},
|
||||
"source": [
|
||||
"### Define common functions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "cac4478ae098"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"\n",
|
||||
"import requests\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_job_name(prefix):\n",
|
||||
" user = os.environ.get(\"USER\")\n",
|
||||
" now = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||
" job_name = f\"{prefix}-{user}-{now}\"\n",
|
||||
" return job_name\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def download_image(url):\n",
|
||||
" response = requests.get(url)\n",
|
||||
" return Image.open(BytesIO(response.content))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_to_base64(image, format=\"JPEG\"):\n",
|
||||
" buffer = BytesIO()\n",
|
||||
" image.save(buffer, format=format)\n",
|
||||
" image_str = base64.b64encode(buffer.getvalue()).decode(\"utf-8\")\n",
|
||||
" return image_str\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def base64_to_image(image_str):\n",
|
||||
" image = Image.open(BytesIO(base64.b64decode(image_str)))\n",
|
||||
" return image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def image_grid(imgs, rows=2, cols=2):\n",
|
||||
" w, h = imgs[0].size\n",
|
||||
" grid = Image.new(\"RGB\", size=(cols * w, rows * h))\n",
|
||||
" for i, img in enumerate(imgs):\n",
|
||||
" grid.paste(img, box=(i % cols * w, i // cols * h))\n",
|
||||
" return grid\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def deploy_model(model_id, task):\n",
|
||||
" model_name = \"vit-gpt2-image-captioning\"\n",
|
||||
" endpoint = aiplatform.Endpoint.create(display_name=f\"{model_name}-endpoint\")\n",
|
||||
" serving_env = {\n",
|
||||
" \"MODEL_ID\": model_id,\n",
|
||||
" \"TASK\": task,\n",
|
||||
" }\n",
|
||||
" # If the model_id is a GCS path, use artifact_uri to pass it to serving docker.\n",
|
||||
" artifact_uri = model_id if model_id.startswith(\"gs://\") else None\n",
|
||||
" model = aiplatform.Model.upload(\n",
|
||||
" display_name=model_name,\n",
|
||||
" serving_container_image_uri=SERVE_DOCKER_URI,\n",
|
||||
" serving_container_ports=[7080],\n",
|
||||
" serving_container_predict_route=\"/predictions/transformers_serving\",\n",
|
||||
" serving_container_health_route=\"/ping\",\n",
|
||||
" serving_container_environment_variables=serving_env,\n",
|
||||
" artifact_uri=artifact_uri,\n",
|
||||
" )\n",
|
||||
" model.deploy(\n",
|
||||
" endpoint=endpoint,\n",
|
||||
" machine_type=\"n1-standard-8\",\n",
|
||||
" accelerator_type=\"NVIDIA_TESLA_T4\",\n",
|
||||
" accelerator_count=1,\n",
|
||||
" deploy_request_timeout=1800,\n",
|
||||
" )\n",
|
||||
" return model, endpoint"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d2d72ecdb8c9"
|
||||
},
|
||||
"source": [
|
||||
"## Upload and deploy models"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "9448c5f545fa"
|
||||
},
|
||||
"source": [
|
||||
"This section uploads the pre-trained model to Model Registry and deploys it on the Endpoint with 1 T4 GPU.\n",
|
||||
"\n",
|
||||
"The model deployment step will take ~15 minutes to complete.\n",
|
||||
"\n",
|
||||
"Once deployed, you can send images to get descriptions."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "b4b46c28d8b1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model, endpoint = deploy_model(\n",
|
||||
" model_id=\"nlpconnect/vit-gpt2-image-captioning\", task=\"image-to-text\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "80b3fd2ace09"
|
||||
},
|
||||
"source": [
|
||||
"NOTE: The model weights will be downloaded after the deployment succeeds. Thus additional 5 minutes of waiting time is needed **after** the above model deployment step succeeds and before you run the next step below. Otherwise you might see a `ServiceUnavailable: 503 502:Bad Gateway` error when you send requests to the endpoint."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "6be655247cb1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"image = download_image(\"http://images.cocodataset.org/val2017/000000039769.jpg\")\n",
|
||||
"display(image)\n",
|
||||
"\n",
|
||||
"instances = [\n",
|
||||
" {\"image\": image_to_base64(image)},\n",
|
||||
"]\n",
|
||||
"preds = endpoint.predict(instances=instances).predictions\n",
|
||||
"print(preds)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "db7ffebdb4be"
|
||||
},
|
||||
"source": [
|
||||
"### Clean up resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2ccf3714dbe9"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_pytorch_vit_gpt2_image_captioning.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -0,0 +1,922 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden TFVision With Image Classification\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_tfvision_image_classification.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_tfvision_image_classification.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_tfvision_image_classification.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [TFVision](https://github.com/tensorflow/models/blob/master/official/vision/MODEL_GARDEN.md) in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [hyperparameter tuning jobs](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) to train new models\n",
|
||||
" * Find and export best models\n",
|
||||
"\n",
|
||||
"* Test trained models\n",
|
||||
" * Upload models to model registry\n",
|
||||
" * Deploy uploaded models\n",
|
||||
" * Run predictions\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab Only\n",
|
||||
"Run the following commands for Colab and skip this section if you use Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"### 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 API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\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": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# The project and bucket are for experiments below.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can choose a region from https://cloud.google.com/about/locations.\n",
|
||||
"# Only regions prefixed by \"us\", \"asia\", or \"europe\" are supported.\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"CHECKPOINT_BUCKET = os.path.join(BUCKET_URI, \"ckpt\")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
|
||||
"\n",
|
||||
"# Download config files.\n",
|
||||
"CONFIG_DIR = os.path.join(BUCKET_URI, \"config\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_config_to_gcs(url):\n",
|
||||
" filename = os.path.basename(url)\n",
|
||||
" destination = os.path.join(CONFIG_DIR, filename)\n",
|
||||
" print(\"Copy\", url, \"to\", destination)\n",
|
||||
" ! wget \"$url\" -O \"$filename\"\n",
|
||||
" ! gsutil cp \"$filename\" \"$destination\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"upload_config_to_gcs(\n",
|
||||
" \"https://raw.githubusercontent.com/tensorflow/models/master/official/vision/configs/experiments/image_classification/imagenet_resnet50_gpu.yaml\"\n",
|
||||
")\n",
|
||||
"upload_config_to_gcs(\n",
|
||||
" \"https://raw.githubusercontent.com/tensorflow/models/master/official/vision/configs/experiments/image_classification/imagenet_resnetrs50_i160_gpu.yaml\"\n",
|
||||
")\n",
|
||||
"upload_config_to_gcs(\n",
|
||||
" \"https://raw.githubusercontent.com/tensorflow/models/master/official/projects/maxvit/configs/experiments/maxvit_base_imagenet_gpu.yaml\"\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"OBJECTIVE = \"icn\"\n",
|
||||
"\n",
|
||||
"# Data converter constants.\n",
|
||||
"DATA_CONVERTER_JOB_PREFIX = \"data_converter\"\n",
|
||||
"DATA_CONVERTER_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/data-converter\"\n",
|
||||
"DATA_CONVERTER_MACHINE_TYPE = \"n1-highmem-8\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Training constants.\n",
|
||||
"TRAINING_JOB_PREFIX = \"train\"\n",
|
||||
"TRAIN_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/tfvision-oss\"\n",
|
||||
"TRAIN_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAIN_ACCELERATOR_TYPE = \"NVIDIA_TESLA_P100\"\n",
|
||||
"TRAIN_NUM_GPU = 1\n",
|
||||
"\n",
|
||||
"# Evaluation constants.\n",
|
||||
"EVALUATION_METRIC = \"accuracy\"\n",
|
||||
"\n",
|
||||
"# Export constants.\n",
|
||||
"EXPORT_JOB_PREFIX = \"export\"\n",
|
||||
"EXPORT_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/tfvision-serving\"\n",
|
||||
"EXPORT_MACHINE_TYPE = \"n1-highmem-8\"\n",
|
||||
"\n",
|
||||
"# Prediction constants.\n",
|
||||
"# You can deploy models with\n",
|
||||
"# pre-build-dockers: https://cloud.google.com/vertex-ai/docs/predictions/pre-built-containers.\n",
|
||||
"# and optimized tensorflow runtime dockers: https://cloud.google.com/vertex-ai/docs/predictions/optimized-tensorflow-runtime.\n",
|
||||
"# The example in this notebook uses optimized tensorflow runtime dockers.\n",
|
||||
"# You can adjust accelerator types and machine types to get faster predictions.\n",
|
||||
"PREDICTION_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/prediction/tf_opt-gpu.2-11:latest\"\n",
|
||||
"SERVING_CONTAINER_ARGS = [\"--allow_precompilation\", \"--allow_compression\"]\n",
|
||||
"PREDICTION_ACCELERATOR_TYPE = \"NVIDIA_TESLA_T4\"\n",
|
||||
"PREDICTION_MACHINE_TYPE = \"n1-standard-4\"\n",
|
||||
"UPLOAD_JOB_PREFIX = \"upload\"\n",
|
||||
"DEPLOY_JOB_PREFIX = \"deploy\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZZFPe_GezXg8"
|
||||
},
|
||||
"source": [
|
||||
"### Define common libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import json\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"from typing import Dict, List, Union\n",
|
||||
"\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy\n",
|
||||
"import tensorflow as tf\n",
|
||||
"import yaml\n",
|
||||
"from google.protobuf import json_format\n",
|
||||
"from google.protobuf.struct_pb2 import Value\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def predict_custom_trained_model(\n",
|
||||
" project: str,\n",
|
||||
" endpoint_id: str,\n",
|
||||
" instances: Union[Dict, List[Dict]],\n",
|
||||
" location: str = \"us-central1\",\n",
|
||||
"):\n",
|
||||
" # The AI Platform services require regional API endpoints.\n",
|
||||
" client_options = {\"api_endpoint\": f\"{location}-aiplatform.googleapis.com\"}\n",
|
||||
" # Initialize client that will be used to create and send requests.\n",
|
||||
" # This client only needs to be created once, and can be reused for multiple requests.\n",
|
||||
" client = aiplatform.gapic.PredictionServiceClient(client_options=client_options)\n",
|
||||
" parameters_dict = {}\n",
|
||||
" parameters = json_format.ParseDict(parameters_dict, Value())\n",
|
||||
" endpoint = client.endpoint_path(\n",
|
||||
" project=project, location=location, endpoint=endpoint_id\n",
|
||||
" )\n",
|
||||
" response = client.predict(\n",
|
||||
" endpoint=endpoint, instances=instances, parameters=parameters\n",
|
||||
" )\n",
|
||||
" return response.predictions, response.deployed_model_id\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_img(path):\n",
|
||||
" img = tf.io.read_file(path)\n",
|
||||
" img = tf.image.decode_jpeg(img, channels=3)\n",
|
||||
" return Image.fromarray(numpy.uint8(img)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def display_image(image):\n",
|
||||
" _ = plt.figure(figsize=(20, 15))\n",
|
||||
" plt.grid(False)\n",
|
||||
" plt.imshow(image)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_prediction_instances(test_filepath, new_width=-1):\n",
|
||||
" if new_width <= 0:\n",
|
||||
" with tf.io.gfile.GFile(test_filepath, \"rb\") as input_file:\n",
|
||||
" encoded_string = base64.b64encode(input_file.read()).decode(\"utf-8\")\n",
|
||||
" else:\n",
|
||||
" img = load_img(test_filepath)\n",
|
||||
" width, height = img.size\n",
|
||||
" print(\"original input image size: \", width, \" , \", height)\n",
|
||||
" new_height = int(height * new_width / width)\n",
|
||||
" new_img = img.resize((new_width, new_height))\n",
|
||||
" print(\"resized input image size: \", new_width, \" , \", new_height)\n",
|
||||
" buffered = BytesIO()\n",
|
||||
" new_img.save(buffered, format=\"JPEG\")\n",
|
||||
" encoded_string = base64.b64encode(buffered.getvalue()).decode(\"utf-8\")\n",
|
||||
"\n",
|
||||
" instances = [\n",
|
||||
" {\n",
|
||||
" \"encoded_image\": {\"b64\": encoded_string},\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" return instances\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_label_map(label_map_yaml_filepath):\n",
|
||||
" with tf.io.gfile.GFile(label_map_yaml_filepath, \"rb\") as input_file:\n",
|
||||
" label_map = yaml.safe_load(input_file.read())\n",
|
||||
" return label_map\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_best_trial(model_dir, max_trial_count, evaluation_metric):\n",
|
||||
" best_trial_dir = \"\"\n",
|
||||
" best_trial_evaluation_results = {}\n",
|
||||
" best_performance = -1\n",
|
||||
"\n",
|
||||
" for i in range(max_trial_count):\n",
|
||||
" current_trial = i + 1\n",
|
||||
" current_trial_dir = os.path.join(model_dir, \"trial_\" + str(current_trial))\n",
|
||||
" current_trial_best_ckpt_dir = os.path.join(current_trial_dir, \"best_ckpt\")\n",
|
||||
" current_trial_best_ckpt_evaluation_filepath = os.path.join(\n",
|
||||
" current_trial_best_ckpt_dir, \"info.json\"\n",
|
||||
" )\n",
|
||||
" with tf.io.gfile.GFile(current_trial_best_ckpt_evaluation_filepath, \"rb\") as f:\n",
|
||||
" eval_metric_results = json.load(f)\n",
|
||||
" current_performance = eval_metric_results[evaluation_metric]\n",
|
||||
" if current_performance > best_performance:\n",
|
||||
" best_performance = current_performance\n",
|
||||
" best_trial_dir = current_trial_dir\n",
|
||||
" best_trial_evaluation_results = eval_metric_results\n",
|
||||
" return best_trial_dir, best_trial_evaluation_results\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_checkpoint_to_gcs(checkpoint_url):\n",
|
||||
" filename = os.path.basename(checkpoint_url)\n",
|
||||
" checkpoint_name = filename.replace(\".tar.gz\", \"\")\n",
|
||||
" print(\"Download checkpoint from\", checkpoint_url, \"and store to\", CHECKPOINT_BUCKET)\n",
|
||||
" ! wget $checkpoint_url -O $filename\n",
|
||||
" ! mkdir -p $checkpoint_name\n",
|
||||
" ! tar -xvzf $filename -C $checkpoint_name\n",
|
||||
"\n",
|
||||
" # Search for relative path to the checkpoint.\n",
|
||||
" checkpoint_path = None\n",
|
||||
" for root, dirs, files in os.walk(checkpoint_name):\n",
|
||||
" for file in files:\n",
|
||||
" if file.endswith(\".index\"):\n",
|
||||
" checkpoint_path = os.path.join(root, os.path.splitext(file)[0])\n",
|
||||
" checkpoint_path = os.path.relpath(checkpoint_path, checkpoint_name)\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" ! gsutil cp -r $checkpoint_name $CHECKPOINT_BUCKET/\n",
|
||||
" checkpoint_uri = os.path.join(CHECKPOINT_BUCKET, checkpoint_name, checkpoint_path)\n",
|
||||
" print(\"Checkpoint uploaded to\", checkpoint_uri)\n",
|
||||
" return checkpoint_uri"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RB_xY9ipr7ZU"
|
||||
},
|
||||
"source": [
|
||||
"## Train new models\n",
|
||||
"This section shows how to train new models.\n",
|
||||
"1. Convert input data to training formats\n",
|
||||
"2. Create hyperparameter tuning jobs to train new models\n",
|
||||
"3. Find and export best models\n",
|
||||
"\n",
|
||||
"If you already trained models, please go to the section `Test Trained models`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare input data for training\n",
|
||||
"\n",
|
||||
"Prepare data in the format as described [here](https://cloud.google.com/vertex-ai/docs/image-data/classification/prepare-data), and then convert them to the training formats as below:\n",
|
||||
"\n",
|
||||
"* `input_file_path`: The input file path for preparing data.\n",
|
||||
"* `input_file_type`: The input file type, such as csv or jsonl.\n",
|
||||
"* `split_ratio`: The proportion of data to split into train/validation/test.\n",
|
||||
"* `num_shard`: The number of shards for train/validation/test.\n",
|
||||
"* `output_dir`: The output directory, which will container prepared train/test/validation data."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This job will convert input data as training format, with given split ratios\n",
|
||||
"# and number of shards on train/test/validation.\n",
|
||||
"\n",
|
||||
"data_converter_job_name = get_job_name_with_datetime(\n",
|
||||
" DATA_CONVERTER_JOB_PREFIX + \"_\" + OBJECTIVE\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"input_file_path = \"\" # @param {type:\"string\"}\n",
|
||||
"input_file_type = \"csv\" # @param [\"csv\", \"jsonl\"]\n",
|
||||
"num_classes = 5 # @param {type:\"integer\"}\n",
|
||||
"split_ratio = \"0.8,0.1,0.1\"\n",
|
||||
"num_shard = \"10,10,10\"\n",
|
||||
"data_converter_output_dir = os.path.join(BUCKET_URI, data_converter_job_name)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": DATA_CONVERTER_MACHINE_TYPE,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": DATA_CONVERTER_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--input_file_path=%s\" % input_file_path,\n",
|
||||
" \"--input_file_type=%s\" % input_file_type,\n",
|
||||
" \"--objective=%s\" % OBJECTIVE,\n",
|
||||
" \"--num_shard=%s\" % num_shard,\n",
|
||||
" \"--split_ratio=%s\" % split_ratio,\n",
|
||||
" \"--output_dir=%s\" % data_converter_output_dir,\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"data_converter_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=data_converter_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"data_converter_custom_job.run()\n",
|
||||
"\n",
|
||||
"input_train_data_path = os.path.join(data_converter_output_dir, \"train.tfrecord*\")\n",
|
||||
"input_validation_data_path = os.path.join(data_converter_output_dir, \"val.tfrecord*\")\n",
|
||||
"label_map_path = os.path.join(data_converter_output_dir, \"label_map.yaml\")\n",
|
||||
"print(\"input_train_data_path for training: \", input_train_data_path)\n",
|
||||
"print(\"input_validation_data_path for training: \", input_validation_data_path)\n",
|
||||
"print(\"label_map_path for prediction: \", label_map_path)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Vertex AI custom job with hyperparameter tuning\n",
|
||||
"\n",
|
||||
"You use the Vertex AI SDK to create and run the hyperparameter tuning job with Vertex AI Model Garden Training Dockers.\n",
|
||||
"\n",
|
||||
"#### Define the following specifications\n",
|
||||
"* `worker_pool_specs`: Dictionary specifying the machine type and Docker image. This example defines a single node cluster with one `n1-standard-4` machine with two `NVIDIA_TESLA_T4` GPUs.\n",
|
||||
"* `parameter_spec`: Dictionary specifying the parameters to optimize. The dictionary key is the string assigned to the command line argument for each hyperparameter in your training application code, and the dictionary value is the parameter specification. The parameter specification includes the type, min/max values, and scale for the hyperparameter.\n",
|
||||
"* `metric_spec`: Dictionary specifying the metric to optimize. The dictionary key is the `hyperparameter_metric_tag` that you set in your training application code, and the value is the optimization goal."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
|
||||
"\n",
|
||||
"# Input train and validation datasets can be found from the section above\n",
|
||||
"# `Convert input data for training`.\n",
|
||||
"# Set prepared datasets if exists.\n",
|
||||
"# input_train_data_path = ''\n",
|
||||
"# input_validation_data_path = ''\n",
|
||||
"\n",
|
||||
"experiment = \"ResNet-50\" # @param [\"ResNet-50\",\"ResNet-RS-50\",\"Efficientnetv2-m\",\"ViT-ti16\",\"ViT-s16\",\"ViT-b16\",\"ViT-l16\", \"MaxViT\"]\n",
|
||||
"\n",
|
||||
"train_job_name = get_job_name_with_datetime(TRAINING_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"model_dir = os.path.join(BUCKET_URI, train_job_name)\n",
|
||||
"\n",
|
||||
"# The arguments here are mainly for test purposes. Please update them\n",
|
||||
"# to get better performances.\n",
|
||||
"common_args = {\n",
|
||||
" \"input_train_data_path\": input_train_data_path,\n",
|
||||
" \"input_validation_data_path\": input_validation_data_path,\n",
|
||||
" \"objective\": OBJECTIVE,\n",
|
||||
" \"model_dir\": model_dir,\n",
|
||||
" \"num_classes\": num_classes,\n",
|
||||
" \"global_batch_size\": 4,\n",
|
||||
" \"prefetch_buffer_size\": 32,\n",
|
||||
" \"train_steps\": 2000,\n",
|
||||
" \"input_size\": \"224,224\",\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Arguments for different experiments.\n",
|
||||
"experiment_container_args_dict = {\n",
|
||||
" \"ResNet-50\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"resnet_imagenet\",\n",
|
||||
" \"config_file\": os.path.join(CONFIG_DIR, \"imagenet_resnet50_gpu.yaml\"),\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/resnet/resnet-50-i224.tar.gz\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"ResNet-RS-50\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"resnet_rs_imagenet\",\n",
|
||||
" \"config_file\": os.path.join(\n",
|
||||
" CONFIG_DIR, \"imagenet_resnetrs50_i160_gpu.yaml\"\n",
|
||||
" ),\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/resnet-rs/resnet-rs-50-i160.tar.gz\",\n",
|
||||
" \"input_size\": \"160,160\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"Efficientnetv2-m\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"hub_model\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"ViT-ti16\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"deit_imagenet_pretrain\",\n",
|
||||
" \"model_name\": \"vit-ti16\",\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/vit/vit-deit-imagenet-ti16.tar.gz\",\n",
|
||||
" \"input_size\": \"224,224\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"ViT-s16\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"deit_imagenet_pretrain\",\n",
|
||||
" \"model_name\": \"vit-s16\",\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/vit/vit-deit-imagenet-s16.tar.gz\",\n",
|
||||
" \"input_size\": \"224,224\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"ViT-b16\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"deit_imagenet_pretrain\",\n",
|
||||
" \"model_name\": \"vit-b16\",\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/vit/vit-deit-imagenet-b16.tar.gz\",\n",
|
||||
" \"input_size\": \"224,224\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"ViT-l16\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"deit_imagenet_pretrain\",\n",
|
||||
" \"model_name\": \"vit-l16\",\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/vit/vit-deit-imagenet-l16.tar.gz\",\n",
|
||||
" \"input_size\": \"224,224\",\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
" \"MaxViT\": dict(\n",
|
||||
" common_args,\n",
|
||||
" **{\n",
|
||||
" \"experiment\": \"maxvit_imagenet\",\n",
|
||||
" \"config_file\": os.path.join(CONFIG_DIR, \"maxvit_base_imagenet_gpu.yaml\"),\n",
|
||||
" },\n",
|
||||
" ),\n",
|
||||
"}\n",
|
||||
"experiment_container_args = experiment_container_args_dict[experiment]\n",
|
||||
"\n",
|
||||
"# Copy checkpoint to GCS bucket if specified.\n",
|
||||
"init_checkpoint = experiment_container_args.get(\"init_checkpoint\")\n",
|
||||
"if init_checkpoint:\n",
|
||||
" experiment_container_args[\"init_checkpoint\"] = upload_checkpoint_to_gcs(\n",
|
||||
" init_checkpoint\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"# Use container that supports MaxViT\n",
|
||||
"if experiment == \"MaxViT\":\n",
|
||||
" TRAIN_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/tfvision-oss-v2\"\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAIN_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAIN_ACCELERATOR_TYPE,\n",
|
||||
" # Each training job uses TRAIN_NUM_GPU GPUs.\n",
|
||||
" \"accelerator_count\": TRAIN_NUM_GPU,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAIN_CONTAINER_URI,\n",
|
||||
" \"args\": [\n",
|
||||
" \"--mode=train_and_eval\",\n",
|
||||
" \"--params_override=runtime.num_gpus=%d\" % TRAIN_NUM_GPU,\n",
|
||||
" ]\n",
|
||||
" + [\"--{}={}\".format(k, v) for k, v in experiment_container_args.items()],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"metric_spec = {\"model_performance\": \"maximize\"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"LEARNING_RATES = [5e-4, 1e-3]\n",
|
||||
"# Models will be trained with each learning rate separately and max trial count is the number of learning rates.\n",
|
||||
"MAX_TRIAL_COUNT = len(LEARNING_RATES)\n",
|
||||
"parameter_spec = {\n",
|
||||
" \"learning_rate\": hpt.DiscreteParameterSpec(values=LEARNING_RATES, scale=\"linear\"),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"print(worker_pool_specs, metric_spec, parameter_spec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"#### Run the hyperparameter tuning job\n",
|
||||
"* `max_trial_count`: Sets an upper bound on the number of trials the service will run. The recommended practice is to start with a smaller number of trials and get a sense of how impactful your chosen hyperparameters are before scaling up.\n",
|
||||
"\n",
|
||||
"* `parallel_trial_count`: If you use parallel trials, the service provisions multiple training processing clusters. The worker pool spec that you specify when creating the job is used for each individual training cluster. Increasing the number of parallel trials reduces the amount of time the hyperparameter tuning job takes to run; however, it can reduce the effectiveness of the job overall. This is because the default tuning strategy uses results of previous trials to inform the assignment of values in subsequent trials.\n",
|
||||
"\n",
|
||||
"* `search_algorithm`: The available search algorithms are grid, random, or default (None). The default option applies Bayesian optimization to search the space of possible hyperparameter values and is the recommended algorithm.\n",
|
||||
"\n",
|
||||
"Click on the generated link in the output to see your run in the Cloud Console."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"train_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_hpt_job = aiplatform.HyperparameterTuningJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" custom_job=train_custom_job,\n",
|
||||
" metric_spec=metric_spec,\n",
|
||||
" parameter_spec=parameter_spec,\n",
|
||||
" max_trial_count=MAX_TRIAL_COUNT,\n",
|
||||
" parallel_trial_count=MAX_TRIAL_COUNT,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" search_algorithm=None,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_hpt_job.run()\n",
|
||||
"\n",
|
||||
"print(\"experiment is: \", experiment)\n",
|
||||
"print(\"model_dir is: \", model_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mV-Djz-frBni"
|
||||
},
|
||||
"source": [
|
||||
"### Export best models as TF Saved Model format"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "09Rz1AYspK19"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This job will export models from TF checkpoints to TF saved model format.\n",
|
||||
"# model_dir is from the section above.\n",
|
||||
"best_trial_dir, best_trial_evaluation_results = get_best_trial(\n",
|
||||
" model_dir, MAX_TRIAL_COUNT, EVALUATION_METRIC\n",
|
||||
")\n",
|
||||
"print(\"best_trial_dir: \", best_trial_dir)\n",
|
||||
"print(\"best_trial_evaluation_results: \", best_trial_evaluation_results)\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": EXPORT_MACHINE_TYPE,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": EXPORT_CONTAINER_URI,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--objective=%s\" % OBJECTIVE,\n",
|
||||
" \"--input_image_size=%s\" % experiment_container_args[\"input_size\"],\n",
|
||||
" \"--experiment=%s\" % experiment_container_args[\"experiment\"],\n",
|
||||
" \"--config_file=%s/params.yaml\" % best_trial_dir,\n",
|
||||
" \"--checkpoint_path=%s/best_ckpt\" % best_trial_dir,\n",
|
||||
" \"--export_dir=%s/best_model\" % model_dir,\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"model_export_name = get_job_name_with_datetime(EXPORT_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"model_export_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=model_export_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"model_export_custom_job.run()\n",
|
||||
"\n",
|
||||
"print(\"best model is saved to: \", os.path.join(model_dir, \"best_model\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"## Test trained models\n",
|
||||
"This section shows how to test with trained models.\n",
|
||||
"1. Upload and deploy models to model registry\n",
|
||||
"2. Run predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Upload and deploy models\n",
|
||||
"# model_dir is from the section above.\n",
|
||||
"trained_model_dir = os.path.join(model_dir, \"best_model/saved_model\")\n",
|
||||
"\n",
|
||||
"upload_job_name = get_job_name_with_datetime(UPLOAD_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"\n",
|
||||
"model = aiplatform.Model.upload(\n",
|
||||
" display_name=upload_job_name,\n",
|
||||
" artifact_uri=trained_model_dir,\n",
|
||||
" serving_container_image_uri=PREDICTION_CONTAINER_URI,\n",
|
||||
" serving_container_args=SERVING_CONTAINER_ARGS,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"model.wait()\n",
|
||||
"\n",
|
||||
"print(\"The uploaded model name is: \", upload_job_name)\n",
|
||||
"\n",
|
||||
"deploy_model_name = get_job_name_with_datetime(DEPLOY_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"print(\"The deployed job name is: \", deploy_model_name)\n",
|
||||
"\n",
|
||||
"endpoint = model.deploy(\n",
|
||||
" deployed_model_display_name=deploy_model_name,\n",
|
||||
" machine_type=PREDICTION_MACHINE_TYPE,\n",
|
||||
" traffic_split={\"0\": 100},\n",
|
||||
" accelerator_type=PREDICTION_ACCELERATOR_TYPE,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" min_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"print(\"endpoint id is: \", endpoint_id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vbIW9me1F2RY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Run predictions\n",
|
||||
"\n",
|
||||
"# endpoint_id was generated in the section above (`Upload and deploy models`).\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"\n",
|
||||
"test_filepath = \"\" # @param {type:\"string\"}\n",
|
||||
"# If the input image is too large, we will resize it for prediction.\n",
|
||||
"instances = get_prediction_instances(test_filepath, new_width=1000)\n",
|
||||
"\n",
|
||||
"# The label map file was generated from the section above (`Convert input data for training`).\n",
|
||||
"label_map = get_label_map(label_map_path)[\"label_map\"]\n",
|
||||
"\n",
|
||||
"predictions, _ = predict_custom_trained_model(\n",
|
||||
" project=PROJECT_ID, location=REGION, endpoint_id=endpoint_id, instances=instances\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"probs = dict(predictions[0])[\"probs\"]\n",
|
||||
"max_prob = max(probs)\n",
|
||||
"max_index = probs.index(max_prob)\n",
|
||||
"print(\"The test image: \", test_filepath)\n",
|
||||
"print(\"max_prob: \", max_prob, \", for label: \", label_map[max_index])\n",
|
||||
"img = load_img(test_filepath)\n",
|
||||
"display_image(img)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()\n",
|
||||
"# Delete custom and hpt jobs.\n",
|
||||
"if data_converter_custom_job.list(filter=f'display_name=\"{data_converter_job_name}\"'):\n",
|
||||
" data_converter_custom_job.delete()\n",
|
||||
"if train_hpt_job.list(filter=f'display_name=\"{train_job_name}\"'):\n",
|
||||
" train_hpt_job.delete()\n",
|
||||
"if model_export_custom_job.list(filter=f'display_name=\"{model_export_name}\"'):\n",
|
||||
" model_export_custom_job.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_tfvision_image_classification.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,980 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ur8xi4C7S06n"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 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",
|
||||
"# You may obtain a copy of the License at\n",
|
||||
"#\n",
|
||||
"# https://www.apache.org/licenses/LICENSE-2.0\n",
|
||||
"#\n",
|
||||
"# Unless required by applicable law or agreed to in writing, software\n",
|
||||
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
|
||||
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
|
||||
"# See the License for the specific language governing permissions and\n",
|
||||
"# limitations under the License."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TirJ-SGQseby"
|
||||
},
|
||||
"source": [
|
||||
"# Vertex AI Model Garden TFVision With Image Segmentation\n",
|
||||
"\n",
|
||||
"<table align=\"left\">\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_tfvision_image_segmentation.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\"> Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_tfvision_image_segmentation.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/notebooks/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/model_garden/model_garden_tfvision_image_segmentation.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>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "dwGLvtIeECLK"
|
||||
},
|
||||
"source": [
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.9"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to use [TFVision](https://github.com/tensorflow/models/blob/master/official/vision/MODEL_GARDEN.md) in Vertex AI Model Garden.\n",
|
||||
"\n",
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"* Train new models\n",
|
||||
" * Convert input data to training formats\n",
|
||||
" * Create [hyperparameter tuning jobs](https://cloud.google.com/vertex-ai/docs/training/hyperparameter-tuning-overview) to train new models\n",
|
||||
" * Find and export best models\n",
|
||||
"\n",
|
||||
"* Test trained models\n",
|
||||
" * Upload models to model registry\n",
|
||||
" * Deploy uploaded models\n",
|
||||
" * Run predictions\n",
|
||||
"\n",
|
||||
"* Cleanup resources\n",
|
||||
"\n",
|
||||
"### Costs\n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
"\n",
|
||||
"* Vertex AI\n",
|
||||
"* Cloud Storage\n",
|
||||
"\n",
|
||||
"Learn about [Vertex AI\n",
|
||||
"pricing](https://cloud.google.com/vertex-ai/pricing) and [Cloud Storage\n",
|
||||
"pricing](https://cloud.google.com/storage/pricing), and use the [Pricing\n",
|
||||
"Calculator](https://cloud.google.com/products/calculator/)\n",
|
||||
"to generate a cost estimate based on your projected usage."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "KEukV6uRk_S3"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "z__i0w0lCAsW"
|
||||
},
|
||||
"source": [
|
||||
"### Colab Only\n",
|
||||
"\n",
|
||||
"Run the following commands for Colab and skip this section if you use Workbench."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Jvqs-ehKlaYh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if \"google.colab\" in str(get_ipython()):\n",
|
||||
" ! pip3 install --upgrade google-cloud-aiplatform\n",
|
||||
"\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
" app = IPython.Application.instance()\n",
|
||||
" app.kernel.do_shutdown(True)\n",
|
||||
"\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
},
|
||||
"source": [
|
||||
"### 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 API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\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": "9wExiMUxFk91"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# The project and bucket are for experiments below.\n",
|
||||
"PROJECT_ID = \"\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = \"\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"# You can choose a region from https://cloud.google.com/about/locations.\n",
|
||||
"# Only regions prefixed by \"us\", \"asia\", or \"europe\" are supported.\n",
|
||||
"REGION = \"europe-west4\" # @param {type:\"string\"}\n",
|
||||
"REGION_PREFIX = REGION.split(\"-\")[0]\n",
|
||||
"assert REGION_PREFIX in (\n",
|
||||
" \"us\",\n",
|
||||
" \"europe\",\n",
|
||||
" \"asia\",\n",
|
||||
"), f'{REGION} is not supported. It must be prefixed by \"us\", \"asia\", or \"europe\".'\n",
|
||||
"\n",
|
||||
"! gcloud config set project $PROJECT_ID\n",
|
||||
"\n",
|
||||
"STAGING_BUCKET = os.path.join(BUCKET_URI, \"temporal\")\n",
|
||||
"CHECKPOINT_BUCKET = os.path.join(BUCKET_URI, \"ckpt\")\n",
|
||||
"\n",
|
||||
"aiplatform.init(project=PROJECT_ID, location=REGION, staging_bucket=STAGING_BUCKET)\n",
|
||||
"\n",
|
||||
"# Download config files.\n",
|
||||
"CONFIG_DIR = os.path.join(BUCKET_URI, \"config\")\n",
|
||||
"! wget https://raw.githubusercontent.com/tensorflow/models/master/official/vision/configs/experiments/semantic_segmentation/deeplabv3plus_resnet101_cityscapes_gpu_multiworker_mirrored.yaml\n",
|
||||
"! gsutil cp deeplabv3plus_resnet101_cityscapes_gpu_multiworker_mirrored.yaml $CONFIG_DIR/"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "n6IFz75WGCam"
|
||||
},
|
||||
"source": [
|
||||
"### Define constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"OBJECTIVE = \"isg\"\n",
|
||||
"\n",
|
||||
"# Data converter constants.\n",
|
||||
"DATA_CONVERTER_JOB_PREFIX = \"data_converter\"\n",
|
||||
"DATA_CONVERTER_CONTAINER = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/data-converter\"\n",
|
||||
"DATA_CONVERTER_MACHINE_TYPE = \"n1-highmem-8\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Training constants.\n",
|
||||
"TRAINING_JOB_PREFIX = \"train\"\n",
|
||||
"TRAIN_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/tfvision-oss\"\n",
|
||||
"TRAIN_MACHINE_TYPE = \"n1-highmem-16\"\n",
|
||||
"TRAIN_ACCELERATOR_TYPE = \"NVIDIA_TESLA_V100\"\n",
|
||||
"TRAIN_NUM_GPU = 2\n",
|
||||
"TRAIN_DEEPLABV3PLUS_CONFIG = os.path.join(\n",
|
||||
" CONFIG_DIR, \"deeplabv3plus_resnet101_cityscapes_gpu_multiworker_mirrored.yaml\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Evaluation constants.\n",
|
||||
"EVALUATION_METRIC = \"mean_iou\"\n",
|
||||
"\n",
|
||||
"# Export constants.\n",
|
||||
"EXPORT_JOB_PREFIX = \"export\"\n",
|
||||
"EXPORT_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/tfvision-serving\"\n",
|
||||
"EXPORT_MACHINE_TYPE = \"n1-highmem-8\"\n",
|
||||
"\n",
|
||||
"# Prediction constants.\n",
|
||||
"# You can deploy models with\n",
|
||||
"# pre-build-dockers: https://cloud.google.com/vertex-ai/docs/predictions/pre-built-containers.\n",
|
||||
"# and optimized tensorflow runtime dockers: https://cloud.google.com/vertex-ai/docs/predictions/optimized-tensorflow-runtime.\n",
|
||||
"# The example in this notebook uses optimized tensorflow runtime dockers.\n",
|
||||
"# You can adjust accelerator types and machine types to get faster predictions.\n",
|
||||
"PREDICTION_CONTAINER_URI = f\"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/prediction/tf_opt-gpu.2-11:latest\"\n",
|
||||
"SERVING_CONTAINER_ARGS = [\"--allow_precompilation\", \"--allow_compression\"]\n",
|
||||
"PREDICTION_ACCELERATOR_TYPE = \"NVIDIA_TESLA_T4\"\n",
|
||||
"PREDICTION_MACHINE_TYPE = \"n1-standard-4\"\n",
|
||||
"UPLOAD_JOB_PREFIX = \"upload\"\n",
|
||||
"DEPLOY_JOB_PREFIX = \"deploy\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ZZFPe_GezXg8"
|
||||
},
|
||||
"source": [
|
||||
"### Define common libraries"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "XcYUGwr-AJGY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import base64\n",
|
||||
"import json\n",
|
||||
"import os\n",
|
||||
"from datetime import datetime\n",
|
||||
"from io import BytesIO\n",
|
||||
"from typing import Dict, List, Union\n",
|
||||
"\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy as np\n",
|
||||
"import tensorflow as tf\n",
|
||||
"import yaml\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"from google.protobuf import json_format\n",
|
||||
"from google.protobuf.struct_pb2 import Value\n",
|
||||
"from PIL import Image\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_job_name_with_datetime(prefix: str):\n",
|
||||
" return prefix + datetime.now().strftime(\"_%Y%m%d_%H%M%S\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def predict_custom_trained_model(\n",
|
||||
" project: str,\n",
|
||||
" endpoint_id: str,\n",
|
||||
" instances: Union[Dict, List[Dict]],\n",
|
||||
" location: str = \"us-central1\",\n",
|
||||
"):\n",
|
||||
" # The AI Platform services require regional API endpoints.\n",
|
||||
" client_options = {\"api_endpoint\": f\"{location}-aiplatform.googleapis.com\"}\n",
|
||||
" # Initialize client that will be used to create and send requests.\n",
|
||||
" # This client only needs to be created once, and can be reused for multiple requests.\n",
|
||||
" client = aiplatform.gapic.PredictionServiceClient(client_options=client_options)\n",
|
||||
" parameters_dict = {}\n",
|
||||
" parameters = json_format.ParseDict(parameters_dict, Value())\n",
|
||||
" endpoint = client.endpoint_path(\n",
|
||||
" project=project, location=location, endpoint=endpoint_id\n",
|
||||
" )\n",
|
||||
" response = client.predict(\n",
|
||||
" endpoint=endpoint, instances=instances, parameters=parameters\n",
|
||||
" )\n",
|
||||
" return response.predictions, response.deployed_model_id\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_img(path):\n",
|
||||
" img = tf.io.read_file(path)\n",
|
||||
" img = tf.image.decode_jpeg(img, channels=3)\n",
|
||||
" return Image.fromarray(np.uint8(img)).convert(\"RGB\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def display_image(original_image, category_image_color, score_image_grayscale):\n",
|
||||
" _, axarr = plt.subplots(1, 3, figsize=(20, 15))\n",
|
||||
" axarr[0].imshow(original_image)\n",
|
||||
" axarr[1].imshow(category_image_color)\n",
|
||||
" axarr[2].imshow(score_image_grayscale.convert(\"RGB\"))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_prediction_instances(test_filepath, new_width=-1):\n",
|
||||
" if new_width <= 0:\n",
|
||||
" with tf.io.gfile.GFile(test_filepath, \"rb\") as input_file:\n",
|
||||
" encoded_string = base64.b64encode(input_file.read()).decode(\"utf-8\")\n",
|
||||
" else:\n",
|
||||
" img = load_img(test_filepath)\n",
|
||||
" width, height = img.size\n",
|
||||
" print(\"original input image size: \", width, \" , \", height)\n",
|
||||
" new_height = int(height * new_width / width)\n",
|
||||
" new_img = img.resize((new_width, new_height))\n",
|
||||
" print(\"resized input image size: \", new_width, \" , \", new_height)\n",
|
||||
" buffered = BytesIO()\n",
|
||||
" new_img.save(buffered, format=\"JPEG\")\n",
|
||||
" encoded_string = base64.b64encode(buffered.getvalue()).decode(\"utf-8\")\n",
|
||||
"\n",
|
||||
" instances = [\n",
|
||||
" {\n",
|
||||
" \"encoded_image\": {\"b64\": encoded_string},\n",
|
||||
" }\n",
|
||||
" ]\n",
|
||||
" return instances\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_label_map(label_map_yaml_filepath):\n",
|
||||
" with tf.io.gfile.GFile(label_map_yaml_filepath, \"rb\") as input_file:\n",
|
||||
" label_map = yaml.safe_load(input_file.read())\n",
|
||||
" return label_map\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_best_trial(model_dir, max_trial_count, evaluation_metric):\n",
|
||||
" best_trial_dir = \"\"\n",
|
||||
" best_trial_evaluation_results = {}\n",
|
||||
" best_performance = -1\n",
|
||||
"\n",
|
||||
" for i in range(max_trial_count):\n",
|
||||
" current_trial = i + 1\n",
|
||||
" current_trial_dir = os.path.join(model_dir, \"trial_\" + str(current_trial))\n",
|
||||
" current_trial_best_ckpt_dir = os.path.join(current_trial_dir, \"best_ckpt\")\n",
|
||||
" current_trial_best_ckpt_evaluation_filepath = os.path.join(\n",
|
||||
" current_trial_best_ckpt_dir, \"info.json\"\n",
|
||||
" )\n",
|
||||
" with tf.io.gfile.GFile(current_trial_best_ckpt_evaluation_filepath, \"rb\") as f:\n",
|
||||
" eval_metric_results = json.load(f)\n",
|
||||
" current_performance = eval_metric_results[evaluation_metric]\n",
|
||||
" if current_performance > best_performance:\n",
|
||||
" best_performance = current_performance\n",
|
||||
" best_trial_dir = current_trial_dir\n",
|
||||
" best_trial_evaluation_results = eval_metric_results\n",
|
||||
" return best_trial_dir, best_trial_evaluation_results\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def create_coco_stuff_label_colormap():\n",
|
||||
" \"\"\"Creates a label colormap used in COCO-Stuff segmentation benchmark.\n",
|
||||
"\n",
|
||||
" Returns:\n",
|
||||
" A colormap for visualizing segmentation results.\n",
|
||||
" \"\"\"\n",
|
||||
" return np.asarray(\n",
|
||||
" [\n",
|
||||
" [54, 178, 118],\n",
|
||||
" [0, 85, 178],\n",
|
||||
" [150, 178, 22],\n",
|
||||
" [107, 0, 0],\n",
|
||||
" [0, 0, 89],\n",
|
||||
" [0, 117, 178],\n",
|
||||
" [47, 178, 124],\n",
|
||||
" [178, 116, 0],\n",
|
||||
" [0, 0, 178],\n",
|
||||
" [79, 178, 92],\n",
|
||||
" [134, 0, 0],\n",
|
||||
" [22, 178, 150],\n",
|
||||
" [178, 87, 0],\n",
|
||||
" [178, 146, 0],\n",
|
||||
" [0, 5, 178],\n",
|
||||
" [0, 0, 125],\n",
|
||||
" [0, 53, 178],\n",
|
||||
" [0, 132, 178],\n",
|
||||
" [111, 178, 60],\n",
|
||||
" [178, 131, 0],\n",
|
||||
" [0, 29, 178],\n",
|
||||
" [178, 109, 0],\n",
|
||||
" [178, 35, 0],\n",
|
||||
" [0, 148, 178],\n",
|
||||
" [9, 172, 163],\n",
|
||||
" [0, 0, 178],\n",
|
||||
" [178, 124, 0],\n",
|
||||
" [178, 102, 0],\n",
|
||||
" [0, 156, 175],\n",
|
||||
" [178, 43, 0],\n",
|
||||
" [0, 0, 170],\n",
|
||||
" [178, 94, 0],\n",
|
||||
" [0, 0, 134],\n",
|
||||
" [67, 178, 105],\n",
|
||||
" [99, 178, 73],\n",
|
||||
" [0, 37, 178],\n",
|
||||
" [86, 178, 86],\n",
|
||||
" [15, 178, 156],\n",
|
||||
" [0, 0, 152],\n",
|
||||
" [178, 21, 0],\n",
|
||||
" [0, 124, 178],\n",
|
||||
" [0, 61, 178],\n",
|
||||
" [178, 50, 0],\n",
|
||||
" [0, 109, 178],\n",
|
||||
" [137, 178, 35],\n",
|
||||
" [0, 13, 178],\n",
|
||||
" [0, 101, 178],\n",
|
||||
" [0, 0, 116],\n",
|
||||
" [0, 45, 178],\n",
|
||||
" [41, 178, 131],\n",
|
||||
" [0, 0, 161],\n",
|
||||
" [178, 72, 0],\n",
|
||||
" [0, 0, 143],\n",
|
||||
" [116, 0, 0],\n",
|
||||
" [28, 178, 143],\n",
|
||||
" [170, 6, 0],\n",
|
||||
" [156, 178, 15],\n",
|
||||
" [89, 0, 0],\n",
|
||||
" [143, 178, 28],\n",
|
||||
" [73, 178, 99],\n",
|
||||
" [118, 178, 54],\n",
|
||||
" [92, 178, 79],\n",
|
||||
" [152, 0, 0],\n",
|
||||
" [178, 153, 0],\n",
|
||||
" [98, 0, 0],\n",
|
||||
" [178, 65, 0],\n",
|
||||
" [60, 178, 111],\n",
|
||||
" [169, 175, 3],\n",
|
||||
" [105, 178, 67],\n",
|
||||
" [178, 13, 0],\n",
|
||||
" [163, 178, 9],\n",
|
||||
" [3, 164, 169],\n",
|
||||
" [125, 0, 0],\n",
|
||||
" [175, 168, 0],\n",
|
||||
" [178, 138, 0],\n",
|
||||
" [178, 28, 0],\n",
|
||||
" [35, 178, 137],\n",
|
||||
" [0, 140, 178],\n",
|
||||
" [0, 0, 98],\n",
|
||||
" [131, 178, 41],\n",
|
||||
" [0, 77, 178],\n",
|
||||
" [0, 0, 107],\n",
|
||||
" [0, 93, 178],\n",
|
||||
" [143, 0, 0],\n",
|
||||
" [178, 58, 0],\n",
|
||||
" [161, 0, 0],\n",
|
||||
" [0, 69, 178],\n",
|
||||
" [178, 160, 0],\n",
|
||||
" [178, 80, 0],\n",
|
||||
" [0, 21, 178],\n",
|
||||
" [124, 178, 47],\n",
|
||||
" [255, 214, 0],\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def parse_segmentation_prediction(prediction):\n",
|
||||
" score_bytes = prediction[\"score_bytes\"]\n",
|
||||
" score_image_grayscale = Image.open(\n",
|
||||
" BytesIO(base64.b64decode(dict(score_bytes)[\"b64\"]))\n",
|
||||
" )\n",
|
||||
" category_bytes = prediction[\"category_bytes\"]\n",
|
||||
" category_image_grayscale = Image.open(\n",
|
||||
" BytesIO(base64.b64decode(dict(category_bytes)[\"b64\"]))\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Visualize category images.\n",
|
||||
" color_map = create_coco_stuff_label_colormap()\n",
|
||||
" category_image_grayscale_np = np.array(category_image_grayscale)\n",
|
||||
" rendered_image_shape = category_image_grayscale_np.shape + (3,)\n",
|
||||
" category_image_color_np = np.zeros(rendered_image_shape, dtype=np.uint8)\n",
|
||||
" unique_labels = np.unique(category_image_grayscale_np)\n",
|
||||
" for label in unique_labels:\n",
|
||||
" if label == 0:\n",
|
||||
" continue\n",
|
||||
" category_image_color_np[category_image_grayscale_np == label] = color_map[\n",
|
||||
" label % len(color_map)\n",
|
||||
" ]\n",
|
||||
" category_image_color = Image.fromarray(category_image_color_np)\n",
|
||||
"\n",
|
||||
" return score_image_grayscale, category_image_color\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def upload_checkpoint_to_gcs(checkpoint_url):\n",
|
||||
" filename = os.path.basename(checkpoint_url)\n",
|
||||
" checkpoint_name = filename.replace(\".tar.gz\", \"\")\n",
|
||||
" print(\"Download checkpoint from\", checkpoint_url, \"and store to\", CHECKPOINT_BUCKET)\n",
|
||||
" ! wget $checkpoint_url -O $filename\n",
|
||||
" ! mkdir -p $checkpoint_name\n",
|
||||
" ! tar -xvzf $filename -C $checkpoint_name\n",
|
||||
"\n",
|
||||
" # Search for relative path to the checkpoint.\n",
|
||||
" checkpoint_path = None\n",
|
||||
" for root, dirs, files in os.walk(checkpoint_name):\n",
|
||||
" for file in files:\n",
|
||||
" if file.endswith(\".index\"):\n",
|
||||
" checkpoint_path = os.path.join(root, os.path.splitext(file)[0])\n",
|
||||
" checkpoint_path = os.path.relpath(checkpoint_path, checkpoint_name)\n",
|
||||
" break\n",
|
||||
"\n",
|
||||
" ! gsutil cp -r $checkpoint_name $CHECKPOINT_BUCKET/\n",
|
||||
" checkpoint_uri = os.path.join(CHECKPOINT_BUCKET, checkpoint_name, checkpoint_path)\n",
|
||||
" print(\"Checkpoint uploaded to\", checkpoint_uri)\n",
|
||||
" return checkpoint_uri"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RB_xY9ipr7ZU"
|
||||
},
|
||||
"source": [
|
||||
"## Train new models\n",
|
||||
"This section shows how to train new models.\n",
|
||||
"1. Convert input data to training formats\n",
|
||||
"2. Create hyperparameter tuning jobs to train new models\n",
|
||||
"3. Find and export best models\n",
|
||||
"\n",
|
||||
"If you already trained models, please go to the section `Test trained models`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare input data for training\n",
|
||||
"\n",
|
||||
"Prepare data in the format as described [here](https://cloud.google.com/vertex-ai/docs/image-data/classification/prepare-data), and then convert them to the training formats as below:\n",
|
||||
"\n",
|
||||
"* `input_file_path`: The input file path in coco json formats.\n",
|
||||
"* `split_ratio`: The proportion of data to split into train/validation/test.\n",
|
||||
"* `num_shard`: The number of shards for train/validation/test.\n",
|
||||
"* `output_dir`: The output directory, which will container prepared train/test/validation data."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "IndQ_m6ddUEM"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This job will convert input data as training format, with given split ratios\n",
|
||||
"# and number of shards on train/test/validation.\n",
|
||||
"data_converter_job_name = get_job_name_with_datetime(\n",
|
||||
" DATA_CONVERTER_JOB_PREFIX + \"_\" + OBJECTIVE\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"input_file_path = \"\" # @param {type:\"string\"}\n",
|
||||
"split_ratio = \"0.8,0.1,0.1\"\n",
|
||||
"num_shard = \"10,10,10\"\n",
|
||||
"data_converter_output_dir = os.path.join(BUCKET_URI, data_converter_job_name)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": DATA_CONVERTER_MACHINE_TYPE,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": DATA_CONVERTER_CONTAINER,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--input_file_path=%s\" % input_file_path,\n",
|
||||
" \"--input_file_type=coco_json\",\n",
|
||||
" \"--objective=%s\" % OBJECTIVE,\n",
|
||||
" \"--num_shard=%s\" % num_shard,\n",
|
||||
" \"--split_ratio=%s\" % split_ratio,\n",
|
||||
" \"--output_dir=%s\" % data_converter_output_dir,\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"data_converter_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=data_converter_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"data_converter_custom_job.run()\n",
|
||||
"\n",
|
||||
"input_train_data_path = os.path.join(data_converter_output_dir, \"train.tfrecord*\")\n",
|
||||
"input_validation_data_path = os.path.join(data_converter_output_dir, \"val.tfrecord*\")\n",
|
||||
"label_map_path = os.path.join(data_converter_output_dir, \"label_map.yaml\")\n",
|
||||
"print(\"input_train_data_path for training: \", input_train_data_path)\n",
|
||||
"print(\"input_validation_data_path for training: \", input_validation_data_path)\n",
|
||||
"print(\"label_map_path for prediction: \", label_map_path)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "S6dU2IrIqW3H"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Vertex AI custom job with hyperparameter tuning\n",
|
||||
"\n",
|
||||
"You use the Vertex AI SDK to create and run the hyperparameter tuning job with Vertex AI Model Garden Training Dockers."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaff6f5be7f6"
|
||||
},
|
||||
"source": [
|
||||
"#### Define the following specifications\n",
|
||||
"* `worker_pool_specs`: Dictionary specifying the machine type and Docker image. This example defines a single node cluster with one `n1-standard-4` machine with two `NVIDIA_TESLA_T4` GPUs.\n",
|
||||
"* `parameter_spec`: Dictionary specifying the parameters to optimize. The dictionary key is the string assigned to the command line argument for each hyperparameter in your training application code, and the dictionary value is the parameter specification. The parameter specification includes the type, min/max values, and scale for the hyperparameter.\n",
|
||||
"* `metric_spec`: Dictionary specifying the metric to optimize. The dictionary key is the `hyperparameter_metric_tag` that you set in your training application code, and the value is the optimization goal."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "um_XKbmpTaHx"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform import hyperparameter_tuning as hpt\n",
|
||||
"\n",
|
||||
"label_map = get_label_map(label_map_path)\n",
|
||||
"num_classes = len(label_map[\"label_map\"]) + 1\n",
|
||||
"\n",
|
||||
"# Input train and validation datasets can be found from the section above\n",
|
||||
"# `Convert input data for training`.\n",
|
||||
"# Set prepared datasets if exists.\n",
|
||||
"# input_train_data_path = ''\n",
|
||||
"# input_validation_data_path = ''\n",
|
||||
"\n",
|
||||
"# Refer to https://github.com/tensorflow/models/blob/master/official/vision/MODEL_GARDEN.md\n",
|
||||
"# for more model details.\n",
|
||||
"experiment = \"deeplabv3plus\" # @param [\"deeplabv3plus\"]\n",
|
||||
"\n",
|
||||
"train_job_name = get_job_name_with_datetime(TRAINING_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"model_dir = os.path.join(BUCKET_URI, train_job_name)\n",
|
||||
"\n",
|
||||
"# The arguments here are mainly for test purposes. Please update them\n",
|
||||
"# to get better performances.\n",
|
||||
"experiment_container_args_dict = {\n",
|
||||
" # deeplabv3plus experiment args.\n",
|
||||
" \"deeplabv3plus\": {\n",
|
||||
" \"experiment\": \"seg_deeplabv3plus_pascal\",\n",
|
||||
" \"config_file\": TRAIN_DEEPLABV3PLUS_CONFIG,\n",
|
||||
" \"input_train_data_path\": input_train_data_path,\n",
|
||||
" \"input_validation_data_path\": input_validation_data_path,\n",
|
||||
" \"objective\": OBJECTIVE,\n",
|
||||
" \"model_dir\": model_dir,\n",
|
||||
" \"num_classes\": num_classes,\n",
|
||||
" \"global_batch_size\": 2,\n",
|
||||
" \"prefetch_buffer_size\": 12,\n",
|
||||
" \"train_steps\": 500,\n",
|
||||
" \"output_size\": \"1024,2048\",\n",
|
||||
" \"init_checkpoint\": \"https://storage.googleapis.com/tf_model_garden/vision/deeplabv3plus/dilated-resnet-101-deeplabv3plus.tar.gz\",\n",
|
||||
" }\n",
|
||||
"}\n",
|
||||
"experiment_container_args = experiment_container_args_dict[experiment]\n",
|
||||
"\n",
|
||||
"# Copy checkpoint to GCS bucket if specified.\n",
|
||||
"init_checkpoint = experiment_container_args.get(\"init_checkpoint\")\n",
|
||||
"if init_checkpoint:\n",
|
||||
" experiment_container_args[\"init_checkpoint\"] = upload_checkpoint_to_gcs(\n",
|
||||
" init_checkpoint\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": TRAIN_MACHINE_TYPE,\n",
|
||||
" \"accelerator_type\": TRAIN_ACCELERATOR_TYPE,\n",
|
||||
" \"accelerator_count\": TRAIN_NUM_GPU,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": TRAIN_CONTAINER_URI,\n",
|
||||
" \"args\": [\n",
|
||||
" \"--mode=train_and_eval\",\n",
|
||||
" ]\n",
|
||||
" + [\"--{}={}\".format(k, v) for k, v in experiment_container_args.items()],\n",
|
||||
" },\n",
|
||||
" },\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"metric_spec = {\"model_performance\": \"maximize\"}\n",
|
||||
"\n",
|
||||
"LEARNING_RATES = [0.001]\n",
|
||||
"# Models will be trained with each learning rate separately and max trial count is the number of learning rates.\n",
|
||||
"MAX_TRIAL_COUNT = len(LEARNING_RATES)\n",
|
||||
"parameter_spec = {\n",
|
||||
" \"learning_rate\": hpt.DiscreteParameterSpec(values=LEARNING_RATES, scale=\"linear\"),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"print(worker_pool_specs, metric_spec, parameter_spec)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "HwcCjwlBTQIz"
|
||||
},
|
||||
"source": [
|
||||
"#### Run hyperparameter tuning jobs\n",
|
||||
"* `max_trial_count`: Sets an upper bound on the number of trials the service will run. The recommended practice is to start with a smaller number of trials and get a sense of how impactful your chosen hyperparameters are before scaling up.\n",
|
||||
"\n",
|
||||
"* `parallel_trial_count`: If you use parallel trials, the service provisions multiple training processing clusters. The worker pool spec that you specify when creating the job is used for each individual training cluster. Increasing the number of parallel trials reduces the amount of time the hyperparameter tuning job takes to run; however, it can reduce the effectiveness of the job overall. This is because the default tuning strategy uses results of previous trials to inform the assignment of values in subsequent trials.\n",
|
||||
"\n",
|
||||
"* `search_algorithm`: The available search algorithms are grid, random, or default (None). The default option applies Bayesian optimization to search the space of possible hyperparameter values and is the recommended algorithm.\n",
|
||||
"\n",
|
||||
"Click on the generated link in the output to see your run in the Cloud Console."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "aec22792ee84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"train_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_hpt_job = aiplatform.HyperparameterTuningJob(\n",
|
||||
" display_name=train_job_name,\n",
|
||||
" custom_job=train_custom_job,\n",
|
||||
" metric_spec=metric_spec,\n",
|
||||
" parameter_spec=parameter_spec,\n",
|
||||
" max_trial_count=MAX_TRIAL_COUNT,\n",
|
||||
" parallel_trial_count=1,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" search_algorithm=None,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"train_hpt_job.run()\n",
|
||||
"\n",
|
||||
"print(\"experiment is: \", experiment)\n",
|
||||
"print(\"model_dir is: \", model_dir)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mV-Djz-frBni"
|
||||
},
|
||||
"source": [
|
||||
"### Export best models as TF Saved Model format"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "09Rz1AYspK19"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This job will export models from TF checkpoints to TF saved model format.\n",
|
||||
"from google.cloud import aiplatform\n",
|
||||
"\n",
|
||||
"# model_dir is from the section above.\n",
|
||||
"best_trial_dir, best_trial_evaluation_results = get_best_trial(\n",
|
||||
" model_dir, MAX_TRIAL_COUNT, EVALUATION_METRIC\n",
|
||||
")\n",
|
||||
"print(\"best_trial_dir: \", best_trial_dir)\n",
|
||||
"print(\"best_trial_evaluation_results: \", best_trial_evaluation_results)\n",
|
||||
"\n",
|
||||
"worker_pool_specs = [\n",
|
||||
" {\n",
|
||||
" \"machine_spec\": {\n",
|
||||
" \"machine_type\": EXPORT_MACHINE_TYPE,\n",
|
||||
" },\n",
|
||||
" \"replica_count\": 1,\n",
|
||||
" \"container_spec\": {\n",
|
||||
" \"image_uri\": EXPORT_CONTAINER_URI,\n",
|
||||
" \"command\": [],\n",
|
||||
" \"args\": [\n",
|
||||
" \"--objective=%s\" % OBJECTIVE,\n",
|
||||
" \"--experiment=%s\" % experiment_container_args[\"experiment\"],\n",
|
||||
" \"--config_file=%s/params.yaml\" % best_trial_dir,\n",
|
||||
" \"--checkpoint_path=%s/best_ckpt\" % best_trial_dir,\n",
|
||||
" \"--export_dir=%s/best_model\" % model_dir,\n",
|
||||
" \"--input_image_size=%s\" % experiment_container_args[\"output_size\"],\n",
|
||||
" ],\n",
|
||||
" },\n",
|
||||
" }\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"model_export_name = get_job_name_with_datetime(EXPORT_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"model_export_custom_job = aiplatform.CustomJob(\n",
|
||||
" display_name=model_export_name,\n",
|
||||
" project=PROJECT_ID,\n",
|
||||
" worker_pool_specs=worker_pool_specs,\n",
|
||||
" staging_bucket=STAGING_BUCKET,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"model_export_custom_job.run()\n",
|
||||
"\n",
|
||||
"print(\"best model is saved to: \", os.path.join(model_dir, \"best_model\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "g0BGaofgsMsy"
|
||||
},
|
||||
"source": [
|
||||
"## Test trained models\n",
|
||||
"This section shows how to test with trained models.\n",
|
||||
"1. Upload and deploy models\n",
|
||||
"2. Run predictions"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "NYuQowyZEtxK"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Upload and deploy models\n",
|
||||
"# model_dir is from the section above.\n",
|
||||
"trained_model_dir = os.path.join(model_dir, \"best_model/saved_model\")\n",
|
||||
"\n",
|
||||
"upload_job_name = get_job_name_with_datetime(UPLOAD_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"\n",
|
||||
"model = aiplatform.Model.upload(\n",
|
||||
" display_name=upload_job_name,\n",
|
||||
" artifact_uri=trained_model_dir,\n",
|
||||
" serving_container_image_uri=PREDICTION_CONTAINER_URI,\n",
|
||||
" serving_container_args=SERVING_CONTAINER_ARGS,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"model.wait()\n",
|
||||
"\n",
|
||||
"print(\"The uploaded model name is: \", upload_job_name)\n",
|
||||
"\n",
|
||||
"deploy_model_name = get_job_name_with_datetime(DEPLOY_JOB_PREFIX + \"_\" + OBJECTIVE)\n",
|
||||
"print(\"The deployed job name is: \", deploy_model_name)\n",
|
||||
"\n",
|
||||
"endpoint = model.deploy(\n",
|
||||
" deployed_model_display_name=deploy_model_name,\n",
|
||||
" machine_type=PREDICTION_MACHINE_TYPE,\n",
|
||||
" traffic_split={\"0\": 100},\n",
|
||||
" accelerator_type=PREDICTION_ACCELERATOR_TYPE,\n",
|
||||
" accelerator_count=1,\n",
|
||||
" min_replica_count=1,\n",
|
||||
" max_replica_count=1,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"print(\"endpoint id is: \", endpoint_id)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "vbIW9me1F2RY"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# @title Run predictions\n",
|
||||
"# endpoint_id was generated in the section above (`Upload and deploy models`).\n",
|
||||
"endpoint_id = endpoint.name\n",
|
||||
"\n",
|
||||
"# The test image file path.\n",
|
||||
"test_filepath = \"\" # @param {type:\"string\"}\n",
|
||||
"score_threshold = 0.5 # @param {type:\"number\"}\n",
|
||||
"# If the input image is too large, we will resize it for prediction.\n",
|
||||
"instances = get_prediction_instances(test_filepath, new_width=1000)\n",
|
||||
"\n",
|
||||
"# The label map file was generated from the section above (`Convert input data for training`).\n",
|
||||
"label_map = get_label_map(label_map_path)[\"label_map\"]\n",
|
||||
"\n",
|
||||
"predictions, _ = predict_custom_trained_model(\n",
|
||||
" project=PROJECT_ID, location=REGION, endpoint_id=endpoint_id, instances=instances\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"score_image_grayscale, category_image_color = parse_segmentation_prediction(\n",
|
||||
" dict(predictions[0])\n",
|
||||
")\n",
|
||||
"display_image(\n",
|
||||
" load_img(test_filepath), category_image_color, score_image_grayscale.convert(\"RGB\")\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkH2nrpdp4sp"
|
||||
},
|
||||
"source": [
|
||||
"## Clean up"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "Ax6vQVZhp9pR"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Undeploy model and delete endpoint.\n",
|
||||
"endpoint.delete(force=True)\n",
|
||||
"# Delete models.\n",
|
||||
"model.delete()\n",
|
||||
"# Delete custom and hpt jobs.\n",
|
||||
"if data_converter_custom_job.list(filter=f'display_name=\"{data_converter_job_name}\"'):\n",
|
||||
" data_converter_custom_job.delete()\n",
|
||||
"if train_hpt_job.list(filter=f'display_name=\"{train_job_name}\"'):\n",
|
||||
" train_hpt_job.delete()\n",
|
||||
"if model_export_custom_job.list(filter=f'display_name=\"{model_export_name}\"'):\n",
|
||||
" model_export_custom_job.delete()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "model_garden_tfvision_image_segmentation.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
+1
-1
@@ -198,7 +198,7 @@
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install --upgrade tensorflow google-cloud-bigquery google-cloud-aiplatform {USER_FLAG} -q --no-warn-conflicts"
|
||||
"! pip3 install --upgrade tensorflow google-cloud-bigquery google-cloud-aiplatform \"shapely<2\" {USER_FLAG} -q --no-warn-conflicts"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
+6
-10
@@ -72,7 +72,7 @@
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"Anomaly detection is the identification of rare obesrvations which deviate significantly from the data using ML. Anomaly detection can be done in many ways. Supervised, unsupervised, graph-based. It is particularly important for certain industries like telecommunications, manufacturing, and financial services.\n",
|
||||
"Anomaly detection is the identification of rare observations which deviate significantly from the data using ML. Anomaly detection can be done in many ways. Supervised, unsupervised, graph-based. It is particularly important for certain industries like telecommunications, manufacturing, and financial services.\n",
|
||||
"\n",
|
||||
"For instance, in a manufacturing scenario, you may collect some sensor data to predict the number remaining cycles before engine failure (TTF). In this way, you can take actionable decisions about maintenance planning."
|
||||
]
|
||||
@@ -397,13 +397,12 @@
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"SRC_PATH = \"src\"\n",
|
||||
"KFP_COMPONENTS_PATH = \"components\"\n",
|
||||
"PIPELINES_PATH = \"pipelines\"\n",
|
||||
"TRAIN_PIPELINES_PATH = os.path.join(PIPELINES_PATH, \"train_pipelines\")\n",
|
||||
"TEST_PIPELINES_PATH = os.path.join(PIPELINES_PATH, \"test_pipelines\")\n",
|
||||
"\n",
|
||||
"! mkdir -m 777 -p {SRC_PATH} {KFP_COMPONENTS_PATH} {TRAIN_PIPELINES_PATH} {TEST_PIPELINES_PATH}"
|
||||
"! mkdir -m 777 -p {KFP_COMPONENTS_PATH} {TRAIN_PIPELINES_PATH} {TEST_PIPELINES_PATH}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -425,14 +424,12 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from urllib.parse import urlparse\n",
|
||||
"\n",
|
||||
"PUBLIC_DATA_URI = (\n",
|
||||
" \" gs://cloud-samples-data/vertex-ai/pipeline-deployment/datasets/turbofan_anomaly\"\n",
|
||||
" \"gs://cloud-samples-data/vertex-ai/pipeline-deployment/datasets/turbofan_anomaly\"\n",
|
||||
")\n",
|
||||
"GCS_TRAIN_URI = urlparse(PUBLIC_DATA_URI)._replace(path=\"train_FD001.csv\").geturl()\n",
|
||||
"GCS_TEST_URI = urlparse(PUBLIC_DATA_URI)._replace(path=\"test_FD001.csv\").geturl()\n",
|
||||
"GCS_LABELS_URI = urlparse(PUBLIC_DATA_URI)._replace(path=\"RUL_FD001.csv\").geturl()"
|
||||
"GCS_TRAIN_URI = f\"{PUBLIC_DATA_URI}/train_FD001.csv\"\n",
|
||||
"GCS_TEST_URI = f\"{PUBLIC_DATA_URI}/test_FD001.csv\"\n",
|
||||
"GCS_LABELS_URI = f\"{PUBLIC_DATA_URI}/RUL_FD001.csv\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1478,7 +1475,6 @@
|
||||
"# Remove local resorces\n",
|
||||
"delete_local_resources = False\n",
|
||||
"if delete_local_resources:\n",
|
||||
" ! rm -rf {SRC_PATH}\n",
|
||||
" ! rm -rf {KFP_COMPONENTS_PATH}\n",
|
||||
" ! rm -rf {TRAIN_PIPELINES_PATH}\n",
|
||||
" ! rm -rf {TEST_PIPELINES_PATH}"
|
||||
|
||||
+95
-41
@@ -54,18 +54,20 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
"id": "239ba71252d3"
|
||||
},
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This notebook shows how to use `Vertex AI Pipelines` and `BigQuery ML pipeline components` to train and evaluate a demand forecasting model.\n",
|
||||
"\n",
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset is a modified version of the dataset in [Build and visualize demand forecast predictions using Datastream, Dataflow, BigQuery ML, and Looker\n",
|
||||
"](https://cloud.google.com/architecture/build-visualize-demand-forecast-prediction-datastream-dataflow-bigqueryml-looker) solution architecture\n",
|
||||
"\n",
|
||||
"This notebook shows how to use `Vertex AI Pipelines` and `BigQuery ML pipeline components` to train and evaluate a demand forecasting model."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "25c28706c23e"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this tutorial, you learn how to train and evaluate a BigQuery ML model using Vertex AI Pipelines and BigQuery ML pipeline components. \n",
|
||||
@@ -87,8 +89,27 @@
|
||||
" - Generate the ARIMA Plus forecasts\n",
|
||||
" - Generate the ARIMA PLUS forecast explainations\n",
|
||||
"- Compile the pipeline.\n",
|
||||
"- Execute the pipeline.\n",
|
||||
"- Execute the pipeline."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "586acfa9b502"
|
||||
},
|
||||
"source": [
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset is a modified version of the dataset in [Build and visualize demand forecast predictions using Datastream, Dataflow, BigQuery ML, and Looker\n",
|
||||
"](https://cloud.google.com/architecture/build-visualize-demand-forecast-prediction-datastream-dataflow-bigqueryml-looker) solution architecture\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
},
|
||||
"source": [
|
||||
"### Costs \n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
@@ -352,9 +373,8 @@
|
||||
"id": "06571eb4063b"
|
||||
},
|
||||
"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 it onto the name of resources you create in this tutorial."
|
||||
"#### UUID\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."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -365,9 +385,16 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"import random\n",
|
||||
"import string\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d%H%M%S\")"
|
||||
"\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()"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -485,7 +512,7 @@
|
||||
"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_NAME = PROJECT_ID + \"-aip-\" + UUID\n",
|
||||
" BUCKET_URI = f\"gs://{BUCKET_NAME}\""
|
||||
]
|
||||
},
|
||||
@@ -706,6 +733,7 @@
|
||||
"KFP_COMPONENTS_PATH = \"components\"\n",
|
||||
"PIPELINES_PATH = \"pipelines\"\n",
|
||||
"\n",
|
||||
"! mkdir -m 777 -p {DATA_PATH}\n",
|
||||
"! mkdir -m 777 -p {KFP_COMPONENTS_PATH}\n",
|
||||
"! mkdir -m 777 -p {PIPELINES_PATH}"
|
||||
]
|
||||
@@ -771,7 +799,7 @@
|
||||
" --location={LOCATION} \\\n",
|
||||
" --source_format=CSV \\\n",
|
||||
" --skip_leading_rows=1\\\n",
|
||||
" fast_fresh.orders_{TIMESTAMP} \\\n",
|
||||
" fast_fresh.orders_{UUID} \\\n",
|
||||
" {RAW_DATA_URI} \\\n",
|
||||
" time_of_sale:DATETIME,order_id:INTEGER,product_name:STRING,price:NUMERIC,quantity:NUMERIC,payment_method:STRING,store_id:INTEGER,user_id:INTEGER"
|
||||
]
|
||||
@@ -782,7 +810,7 @@
|
||||
"id": "ZrgOD30o7HcL"
|
||||
},
|
||||
"source": [
|
||||
"## BQML Training Formalization\n",
|
||||
"## BigQuery ML Training Formalization\n",
|
||||
"\n",
|
||||
"In the next cells, you build the components and pipeline to train and evaluate the BQML demand forecasting model."
|
||||
]
|
||||
@@ -820,13 +848,13 @@
|
||||
"BQ_EVALUATE_MODEL_TABLE_PREFIX = \"orders_arima_model_evaluate\"\n",
|
||||
"BQ_FORECAST_TABLE_PREFIX = \"orders_arima_forecast\"\n",
|
||||
"BQ_EXPLAIN_FORECAST_TABLE_PREFIX = \"orders_arima_explain_forecast\"\n",
|
||||
"BQ_ORDERS_TABLE = f\"{BQ_ORDERS_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_TRAINING_TABLE = f\"{BQ_TRAINING_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_MODEL_TABLE = f\"{BQ_MODEL_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_EVALUATE_TS_TABLE = f\"{BQ_EVALUATE_TS_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_EVALUATE_MODEL_TABLE = f\"{BQ_EVALUATE_MODEL_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_FORECAST_TABLE = f\"{BQ_FORECAST_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_EXPLAIN_FORECAST_TABLE = f\"{BQ_EXPLAIN_FORECAST_TABLE_PREFIX}_{TIMESTAMP}\"\n",
|
||||
"BQ_ORDERS_TABLE = f\"{BQ_ORDERS_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"BQ_TRAINING_TABLE = f\"{BQ_TRAINING_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"BQ_MODEL_TABLE = f\"{BQ_MODEL_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"BQ_EVALUATE_TS_TABLE = f\"{BQ_EVALUATE_TS_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"BQ_EVALUATE_MODEL_TABLE = f\"{BQ_EVALUATE_MODEL_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"BQ_FORECAST_TABLE = f\"{BQ_FORECAST_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"BQ_EXPLAIN_FORECAST_TABLE = f\"{BQ_EXPLAIN_FORECAST_TABLE_PREFIX}_{UUID}\"\n",
|
||||
"\n",
|
||||
"BQ_TRAIN_CONFIGURATION = {\n",
|
||||
" \"destinationTable\": {\n",
|
||||
@@ -1022,7 +1050,7 @@
|
||||
"id": "pcSL1FHk69KT"
|
||||
},
|
||||
"source": [
|
||||
"### Build the BQML training pipeline\n",
|
||||
"### Build the BigQuery ML training pipeline\n",
|
||||
"\n",
|
||||
"Define your workflow using Kubeflow Pipelines DSL package. \n",
|
||||
"\n",
|
||||
@@ -1094,8 +1122,8 @@
|
||||
" location=location,\n",
|
||||
" ).set_display_name(\"get train data\")\n",
|
||||
"\n",
|
||||
" # Train the ARIMA PLUS model\n",
|
||||
" bq_arima_model_op = (\n",
|
||||
" # Run an ARIMA PLUS experiment\n",
|
||||
" bq_arima_model_exp_op = (\n",
|
||||
" BigqueryCreateModelJobOp(\n",
|
||||
" query=f\"\"\"\n",
|
||||
" -- create model table\n",
|
||||
@@ -1104,10 +1132,7 @@
|
||||
" MODEL_TYPE = \\'ARIMA_PLUS\\',\n",
|
||||
" TIME_SERIES_TIMESTAMP_COL = \\'hourly_timestamp\\',\n",
|
||||
" TIME_SERIES_DATA_COL = \\'total_sold\\',\n",
|
||||
" TIME_SERIES_ID_COL = [\\'product_name\\'],\n",
|
||||
" MODEL_REGISTRY = \\'vertex_ai\\',\n",
|
||||
" VERTEX_AI_MODEL_ID = \\'order_demand_forecasting\\',\n",
|
||||
" VERTEX_AI_MODEL_VERSION_ALIASES = [\\'staging\\']\n",
|
||||
" TIME_SERIES_ID_COL = [\\'product_name\\']\n",
|
||||
" ) AS\n",
|
||||
" SELECT\n",
|
||||
" hourly_timestamp,\n",
|
||||
@@ -1119,7 +1144,7 @@
|
||||
" project=project,\n",
|
||||
" location=location,\n",
|
||||
" )\n",
|
||||
" .set_display_name(\"train arima plus model\")\n",
|
||||
" .set_display_name(\"run arima+ model experiment\")\n",
|
||||
" .after(create_training_dataset_op)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
@@ -1128,12 +1153,12 @@
|
||||
" BigqueryMLArimaEvaluateJobOp(\n",
|
||||
" project=project,\n",
|
||||
" location=location,\n",
|
||||
" model=bq_arima_model_op.outputs[\"model\"],\n",
|
||||
" model=bq_arima_model_exp_op.outputs[\"model\"],\n",
|
||||
" show_all_candidate_models=False,\n",
|
||||
" job_configuration_query=bq_evaluate_time_series_configuration,\n",
|
||||
" )\n",
|
||||
" .set_display_name(\"evaluate arima plus time series\")\n",
|
||||
" .after(bq_arima_model_op)\n",
|
||||
" .after(bq_arima_model_exp_op)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Evaluate ARIMA Plus model\n",
|
||||
@@ -1141,12 +1166,12 @@
|
||||
" BigqueryEvaluateModelJobOp(\n",
|
||||
" project=project,\n",
|
||||
" location=location,\n",
|
||||
" model=bq_arima_model_op.outputs[\"model\"],\n",
|
||||
" model=bq_arima_model_exp_op.outputs[\"model\"],\n",
|
||||
" query_statement=f\"\"\"SELECT * FROM `{project}.{bq_dataset}.{bq_training_table}` WHERE split='TEST'\"\"\",\n",
|
||||
" job_configuration_query=bq_evaluate_model_configuration,\n",
|
||||
" )\n",
|
||||
" .set_display_name(\"evaluate arima plus model\")\n",
|
||||
" .after(bq_arima_model_op)\n",
|
||||
" .after(bq_arima_model_exp_op)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Plot model metrics\n",
|
||||
@@ -1164,6 +1189,34 @@
|
||||
" < PERF_THRESHOLD,\n",
|
||||
" name=\"avg. mae good\",\n",
|
||||
" ):\n",
|
||||
" # Train the ARIMA PLUS model\n",
|
||||
" bq_arima_model_op = (\n",
|
||||
" BigqueryCreateModelJobOp(\n",
|
||||
" query=f\"\"\"\n",
|
||||
" -- create model table\n",
|
||||
" CREATE OR REPLACE MODEL `{project}.{bq_dataset}.{bq_model_table}`\n",
|
||||
" OPTIONS(\n",
|
||||
" MODEL_TYPE = \\'ARIMA_PLUS\\',\n",
|
||||
" TIME_SERIES_TIMESTAMP_COL = \\'hourly_timestamp\\',\n",
|
||||
" TIME_SERIES_DATA_COL = \\'total_sold\\',\n",
|
||||
" TIME_SERIES_ID_COL = [\\'product_name\\'],\n",
|
||||
" MODEL_REGISTRY = \\'vertex_ai\\',\n",
|
||||
" VERTEX_AI_MODEL_ID = \\'order_demand_forecasting\\',\n",
|
||||
" VERTEX_AI_MODEL_VERSION_ALIASES = [\\'staging\\']\n",
|
||||
" ) AS\n",
|
||||
" SELECT\n",
|
||||
" DATETIME_TRUNC(time_of_sale, HOUR) as hourly_timestamp,\n",
|
||||
" product_name,\n",
|
||||
" SUM(quantity) AS total_sold,\n",
|
||||
" FROM `{project}.{bq_dataset}.{bq_orders_table}`\n",
|
||||
" GROUP BY hourly_timestamp, product_name;\n",
|
||||
" \"\"\",\n",
|
||||
" project=project,\n",
|
||||
" location=location,\n",
|
||||
" )\n",
|
||||
" .set_display_name(\"train arima+ model\")\n",
|
||||
" .after(get_evaluation_model_metrics_op)\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # Generate the ARIMA PLUS forecasts\n",
|
||||
" bq_arima_forecast_op = (\n",
|
||||
@@ -1224,7 +1277,7 @@
|
||||
"source": [
|
||||
"### Execute your pipeline\n",
|
||||
"\n",
|
||||
"Next, you execute the pipeline. It takes the following parameters which we set as default:\n",
|
||||
"Next, we execute the pipeline. It takes the following parameters which we set as default:\n",
|
||||
"\n",
|
||||
"- `bq_dataset`: The BigQuery dataset to train on.\n",
|
||||
"- `bq_orders_table` : The BigQuery table of raw data.\n",
|
||||
@@ -1266,7 +1319,7 @@
|
||||
"source": [
|
||||
"### View BigQuery ML training pipeline results\n",
|
||||
"\n",
|
||||
"Finally, you will view the artifact outputs of each task in the pipeline."
|
||||
"Finally, you view the artifact outputs of each task in the pipeline."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1342,8 +1395,8 @@
|
||||
"print(\"bigquery-ml-arima-evaluate-job\")\n",
|
||||
"artifacts = print_pipeline_output(bqml_pipeline, \"bigquery-ml-arima-evaluate-job\")\n",
|
||||
"print(\"\\n\\n\")\n",
|
||||
"print(\"get-model-evaluation-metrics\")\n",
|
||||
"artifacts = print_pipeline_output(bqml_pipeline, \"get-model-evaluation-metrics\")\n",
|
||||
"print(\"bigquery-evaluate-model-job\")\n",
|
||||
"artifacts = print_pipeline_output(bqml_pipeline, \"bigquery-evaluate-model-job\")\n",
|
||||
"print(\"\\n\\n\")\n",
|
||||
"print(\"bigquery-forecast-model-job\")\n",
|
||||
"artifacts = print_pipeline_output(bqml_pipeline, \"bigquery-forecast-model-job\")\n",
|
||||
@@ -1407,7 +1460,8 @@
|
||||
"\n",
|
||||
"# Remove local resorces\n",
|
||||
"! rm -rf {KFP_COMPONENTS_PATH}\n",
|
||||
"! rm -rf {PIPELINES_PATH}"
|
||||
"! rm -rf {PIPELINES_PATH}\n",
|
||||
"! rm -rf {DATA_PATH}"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user