mirror of
https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
synced 2026-09-26 14:42:04 +00:00
45 KiB
45 KiB
In [ ]:
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.In [ ]:
COHERE_API_KEY = "{API KEY}"
GOOGLE_PROJECT_ID = "{Project ID}"
NETWORK_NAME = "{Network Name}"
PEERING_RANGE_NAME = "{Range Name}"
BUCKET_NAME = "gs://{Bucket Name}"
REGION = "us-central1"In [ ]:
PROJECT_ID = GOOGLE_PROJECT_ID # @param {type:"string"}
# Create a VPC network
! gcloud compute networks create {NETWORK_NAME} --bgp-routing-mode=regional --subnet-mode=auto --project={PROJECT_ID}
# Add necessary firewall rules
! gcloud compute firewall-rules create {NETWORK_NAME}-allow-icmp --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow icmp
! 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
! gcloud compute firewall-rules create {NETWORK_NAME}-allow-rdp --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow tcp:3389
! gcloud compute firewall-rules create {NETWORK_NAME}-allow-ssh --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow tcp:22
# Reserve IP range
! 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."
# Set up peering with service networking
! gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com --network={NETWORK_NAME} --ranges={PEERING_RANGE_NAME} --project={PROJECT_ID}In [ ]:
! pip install -U grpcio-tools --user
! pip install -U tensorflow==2.9.1 --user
! pip install -U tensorflow-datasets --userIn [ ]:
! pip install -U git+https://github.com/googleapis/python-aiplatform.git@main-test --userIn [ ]:
! pip install -U cohere --userIn [ ]:
# Automatically restart kernel after installs
import os
if not os.getenv("IS_TESTING"):
# Automatically restart kernel after installs
import IPython
app = IPython.Application.instance()
app.kernel.do_shutdown(True)In [ ]:
# Lets start by loading the dataset with tensorflow-datasets
import tensorflow_datasets as tfds
dataset = tfds.load("ag_news_subset", split="train", shuffle_files=True)In [ ]:
# For speed and cost considerations, lets limit the dataset to 1000 examples
df = tfds.as_dataframe(dataset.take(1000), tfds.builder("ag_news_subset").info)
df["text"] = df["description"].apply(lambda x: x.decode())In [ ]:
# Finally, lets import cohere and use co.embed to create representations for these 1000 articles
import cohere
co = cohere.Client(COHERE_API_KEY)In [ ]:
# running each of the examples through the embedding endpoint
response = co.embed(model="small", texts=list(df["text"].values))
cohere_embeddings = response.embeddingsIn [ ]:
import os
PROJECT_ID = GOOGLE_PROJECT_ID
# Get your Google Cloud project ID from gcloud
if not os.getenv("IS_TESTING"):
shell_output = !gcloud config list --format 'value(core.project)' 2>/dev/null
PROJECT_ID = shell_output[0]
print("Project ID: ", PROJECT_ID)In [ ]:
if PROJECT_ID == "" or PROJECT_ID is None:
PROJECT_ID = GOOGLE_PROJECT_ID # @param {type:"string"}In [ ]:
from datetime import datetime
TIMESTAMP = datetime.now().strftime("%Y%m%d%H%M%S")
if BUCKET_NAME == "" or BUCKET_NAME is None or BUCKET_NAME == "gs://[your-bucket-name]":
BUCKET_NAME = "gs://" + PROJECT_ID + "aip-" + TIMESTAMPIn [ ]:
! gsutil mb -l $REGION $BUCKET_NAMEIn [ ]:
# this will not return anything if the bucket is empty
! gsutil ls -al $BUCKET_NAMEIn [ ]:
import time
import grpc
from google.cloud import aiplatform_v1beta1
from google.protobuf import struct_pb2In [ ]:
ENDPOINT = "{}-aiplatform.googleapis.com".format(REGION)
AUTH_TOKEN = !gcloud auth print-access-token
PROJECT_NUMBER = !gcloud projects list --filter="PROJECT_ID:'{PROJECT_ID}'" --format='value(PROJECT_NUMBER)'
PROJECT_NUMBER = PROJECT_NUMBER[0]
PARENT = "projects/{}/locations/{}".format(PROJECT_ID, REGION)
print("ENDPOINT: {}".format(ENDPOINT))
print("PROJECT_ID: {}".format(PROJECT_ID))
print("REGION: {}".format(REGION))
!gcloud config set project {PROJECT_ID}
!gcloud config set ai_platform/region {REGION}In [ ]:
# This converts the list of embeddings to the json format expected by Matching Engine
with open("cohere_embeddings.json", "w") as f:
for i, e in enumerate(cohere_embeddings):
f.write('{"id":"' + str(i) + '",')
f.write('"embedding":' + str(e) + "}")
f.write("\n")In [ ]:
# NOTE: Everything in this GCS DIR will be DELETED before uploading the data.
# A CommandException is expected if no data is present
! gsutil rm -rf {BUCKET_NAME}/*In [ ]:
! gsutil cp cohere_embeddings.json {BUCKET_NAME}/cohere_embeddings.jsonIn [ ]:
! gsutil ls {BUCKET_NAME}In [ ]:
index_client = aiplatform_v1beta1.IndexServiceClient(
client_options=dict(api_endpoint=ENDPOINT)
)In [ ]:
# Cohere small model is 1024 dimensions, update the dimension size if another model is being used
DIMENSIONS = 1024
DISPLAY_NAME = "cohere_embeddings"
DISPLAY_NAME_BRUTE_FORCE = DISPLAY_NAME + "_brute_force"In [ ]:
treeAhConfig = struct_pb2.Struct(
fields={
"leafNodeEmbeddingCount": struct_pb2.Value(number_value=500),
"leafNodesToSearchPercent": struct_pb2.Value(number_value=7),
}
)
algorithmConfig = struct_pb2.Struct(
fields={"treeAhConfig": struct_pb2.Value(struct_value=treeAhConfig)}
)
config = struct_pb2.Struct(
fields={
"dimensions": struct_pb2.Value(number_value=DIMENSIONS),
"approximateNeighborsCount": struct_pb2.Value(number_value=150),
"distanceMeasureType": struct_pb2.Value(string_value="DOT_PRODUCT_DISTANCE"),
"algorithmConfig": struct_pb2.Value(struct_value=algorithmConfig),
}
)
metadata = struct_pb2.Struct(
fields={
"config": struct_pb2.Value(struct_value=config),
"contentsDeltaUri": struct_pb2.Value(string_value=BUCKET_NAME),
}
)
ann_index = {
"display_name": DISPLAY_NAME,
"description": "Glove 100 ANN index",
"metadata": struct_pb2.Value(struct_value=metadata),
}In [ ]:
ann_index = index_client.create_index(parent=PARENT, index=ann_index)In [ ]:
# Poll the operation until it's done successfullly.
# This will take some time (~30 minutes)
while True:
if ann_index.done():
break
print("Poll the operation to create index...")
time.sleep(60)In [ ]:
INDEX_RESOURCE_NAME = ann_index.result().name
INDEX_RESOURCE_NAMEIn [ ]:
algorithmConfig = struct_pb2.Struct(
fields={"bruteForceConfig": struct_pb2.Value(struct_value=struct_pb2.Struct())}
)
config = struct_pb2.Struct(
fields={
"dimensions": struct_pb2.Value(number_value=DIMENSIONS),
"approximateNeighborsCount": struct_pb2.Value(number_value=150),
"distanceMeasureType": struct_pb2.Value(string_value="DOT_PRODUCT_DISTANCE"),
"algorithmConfig": struct_pb2.Value(struct_value=algorithmConfig),
}
)
metadata = struct_pb2.Struct(
fields={
"config": struct_pb2.Value(struct_value=config),
"contentsDeltaUri": struct_pb2.Value(string_value=BUCKET_NAME),
}
)
brute_force_index = {
"display_name": DISPLAY_NAME_BRUTE_FORCE,
"description": "Glove 100 index (brute force)",
"metadata": struct_pb2.Value(struct_value=metadata),
}In [ ]:
brute_force_index = index_client.create_index(parent=PARENT, index=brute_force_index)In [ ]:
# Poll the operation until it's done successfullly.
# This will take ~45 min.
while True:
if brute_force_index.done():
break
print("Poll the operation to create index...")
time.sleep(60)In [ ]:
INDEX_BRUTE_FORCE_RESOURCE_NAME = brute_force_index.result().name
INDEX_BRUTE_FORCE_RESOURCE_NAMEIn [ ]:
index_endpoint_client = aiplatform_v1beta1.IndexEndpointServiceClient(
client_options=dict(api_endpoint=ENDPOINT)
)In [ ]:
VPC_NETWORK_NAME = "projects/{}/global/networks/{}".format(PROJECT_NUMBER, NETWORK_NAME)
VPC_NETWORK_NAMEIn [ ]:
index_endpoint = {
"display_name": "index_endpoint_for_demo",
"network": VPC_NETWORK_NAME,
}In [ ]:
r = index_endpoint_client.create_index_endpoint(
parent=PARENT, index_endpoint=index_endpoint
)In [ ]:
r.result()In [ ]:
INDEX_ENDPOINT_NAME = r.result().name
INDEX_ENDPOINT_NAMEIn [ ]:
DEPLOYED_INDEX_ID = "cohere_embedding_deployed"In [ ]:
deploy_ann_index = {
"id": DEPLOYED_INDEX_ID,
"display_name": DEPLOYED_INDEX_ID,
"index": INDEX_RESOURCE_NAME,
}In [ ]:
r = index_endpoint_client.deploy_index(
index_endpoint=INDEX_ENDPOINT_NAME, deployed_index=deploy_ann_index
)In [ ]:
# Poll the operation until it's done successfullly.
while True:
if r.done():
break
print("Poll the operation to deploy index...")
time.sleep(60)In [ ]:
r.result()In [ ]:
DEPLOYED_BRUTE_FORCE_INDEX_ID = "cohere_brute_force_deployed"In [ ]:
deploy_brute_force_index = {
"id": DEPLOYED_BRUTE_FORCE_INDEX_ID,
"display_name": DEPLOYED_BRUTE_FORCE_INDEX_ID,
"index": INDEX_BRUTE_FORCE_RESOURCE_NAME,
}In [ ]:
r = index_endpoint_client.deploy_index(
index_endpoint=INDEX_ENDPOINT_NAME, deployed_index=deploy_brute_force_index
)In [ ]:
# Poll the operation until it's done successfullly.
while True:
if r.done():
break
print("Poll the operation to deploy index...")
time.sleep(60)In [ ]:
r.result()In [ ]:
!git clone https://github.com/googleapis/googleapis.gitIn [ ]:
%%writefile match_service.proto
syntax = "proto3";
package google.cloud.aiplatform.container.v1beta1;
import "google/rpc/status.proto";
// MatchService is a Google managed service for efficient vector similarity
// search at scale.
service MatchService {
// Returns the nearest neighbors for the query. If it is a sharded
// deployment, calls the other shards and aggregates the responses.
rpc Match(MatchRequest) returns (MatchResponse) {}
// Returns the nearest neighbors for batch queries. If it is a sharded
// deployment, calls the other shards and aggregates the responses.
rpc BatchMatch(BatchMatchRequest) returns (BatchMatchResponse) {}
}
// Parameters for a match query.
message MatchRequest {
// The ID of the DeploydIndex that will serve the request.
// This MatchRequest is sent to a specific IndexEndpoint of the Control API,
// as per the IndexEndpoint.network. That IndexEndpoint also has
// IndexEndpoint.deployed_indexes, and each such index has an
// DeployedIndex.id field.
// The value of the field below must equal one of the DeployedIndex.id
// fields of the IndexEndpoint that is being called for this request.
string deployed_index_id = 1;
// The embedding values.
repeated float float_val = 2;
// The number of nearest neighbors to be retrieved from database for
// each query. If not set, will use the default from
// the service configuration.
int32 num_neighbors = 3;
// The list of restricts.
repeated Namespace restricts = 4;
// Crowding is a constraint on a neighbor list produced by nearest neighbor
// search requiring that no more than some value k' of the k neighbors
// returned have the same value of crowding_attribute.
// It's used for improving result diversity.
// This field is the maximum number of matches with the same crowding tag.
int32 per_crowding_attribute_num_neighbors = 5;
// The number of neighbors to find via approximate search before
// exact reordering is performed. If not set, the default value from scam
// config is used; if set, this value must be > 0.
int32 approx_num_neighbors = 6;
// The fraction of the number of leaves to search, set at query time allows
// user to tune search performance. This value increase result in both search
// accuracy and latency increase. The value should be between 0.0 and 1.0. If
// not set or set to 0.0, query uses the default value specified in
// NearestNeighborSearchConfig.TreeAHConfig.leaf_nodes_to_search_percent.
int32 leaf_nodes_to_search_percent_override = 7;
}
// Response of a match query.
message MatchResponse {
message Neighbor {
// The ids of the matches.
string id = 1;
// The distances of the matches.
double distance = 2;
}
// All its neighbors.
repeated Neighbor neighbor = 1;
}
// Parameters for a batch match query.
message BatchMatchRequest {
// Batched requests against one index.
message BatchMatchRequestPerIndex {
// The ID of the DeploydIndex that will serve the request.
string deployed_index_id = 1;
// The requests against the index identified by the above deployed_index_id.
repeated MatchRequest requests = 2;
// Selects the optimal batch size to use for low-level batching. Queries
// within each low level batch are executed sequentially while low level
// batches are executed in parallel.
// This field is optional, defaults to 0 if not set. A non-positive number
// disables low level batching, i.e. all queries are executed sequentially.
int32 low_level_batch_size = 3;
}
// The batch requests grouped by indexes.
repeated BatchMatchRequestPerIndex requests = 1;
}
// Response of a batch match query.
message BatchMatchResponse {
// Batched responses for one index.
message BatchMatchResponsePerIndex {
// The ID of the DeployedIndex that produced the responses.
string deployed_index_id = 1;
// The match responses produced by the index identified by the above
// deployed_index_id. This field is set only when the query against that
// index succeed.
repeated MatchResponse responses = 2;
// The status of response for the batch query identified by the above
// deployed_index_id.
google.rpc.Status status = 3;
}
// The batched responses grouped by indexes.
repeated BatchMatchResponsePerIndex responses = 1;
}
// Namespace specifies the rules for determining the datapoints that are
// eligible for each matching query, overall query is an AND across namespaces.
message Namespace {
// The string name of the namespace that this proto is specifying,
// such as "color", "shape", "geo", or "tags".
string name = 1;
// The allowed tokens in the namespace.
repeated string allow_tokens = 2;
// The denied tokens in the namespace.
// The denied tokens have exactly the same format as the token fields, but
// represents a negation. When a token is denied, then matches will be
// excluded whenever the other datapoint has that token.
//
// For example, if a query specifies {color: red, blue, !purple}, then that
// query will match datapoints that are red or blue, but if those points are
// also purple, then they will be excluded even if they are red/blue.
repeated string deny_tokens = 3;
}In [ ]:
! python -m grpc_tools.protoc -I=. --proto_path=googleapis --python_out=. --grpc_python_out=. match_service.protoIn [ ]:
DEPLOYED_INDEX_SERVER_IP = (
list(index_endpoint_client.list_index_endpoints(parent=PARENT))[0]
.deployed_indexes[0]
.private_endpoints.match_grpc_address
)
DEPLOYED_INDEX_SERVER_IPIn [ ]:
import match_service_pb2
import match_service_pb2_grpc
channel = grpc.insecure_channel("{}:10000".format(DEPLOYED_INDEX_SERVER_IP))
stub = match_service_pb2_grpc.MatchServiceStub(channel)In [ ]:
raw_query = "Articles about the climate"In [ ]:
query = co.embed(model="small", texts=[raw_query]).embeddings[0]In [ ]:
# Test query
request = match_service_pb2.MatchRequest()
request.deployed_index_id = DEPLOYED_INDEX_ID
for val in query:
request.float_val.append(val)
response = stub.Match(request)
responseIn [ ]:
index_client.delete_index(name=INDEX_RESOURCE_NAME)In [ ]:
index_endpoint_client.delete_index_endpoint(name=INDEX_ENDPOINT_NAME)