|
17 | 17 | import proto
|
18 | 18 | from typing import Dict, List, NamedTuple, Optional, Sequence, Tuple, Union
|
19 | 19 |
|
| 20 | +from google.api_core import operation |
20 | 21 | from google.auth import credentials as auth_credentials
|
21 | 22 |
|
22 | 23 | from google.cloud.aiplatform import base
|
|
35 | 36 | endpoint_v1 as gca_endpoint_v1,
|
36 | 37 | endpoint_v1beta1 as gca_endpoint_v1beta1,
|
37 | 38 | explanation_v1beta1 as gca_explanation_v1beta1,
|
| 39 | + io as gca_io_compat, |
38 | 40 | machine_resources as gca_machine_resources_compat,
|
39 | 41 | machine_resources_v1beta1 as gca_machine_resources_v1beta1,
|
40 | 42 | model as gca_model_compat,
|
| 43 | + model_service as gca_model_service_compat, |
41 | 44 | model_v1beta1 as gca_model_v1beta1,
|
42 | 45 | env_var as gca_env_var_compat,
|
43 | 46 | env_var_v1beta1 as gca_env_var_v1beta1,
|
@@ -1217,6 +1220,26 @@ def description(self):
|
1217 | 1220 | """Description of the model."""
|
1218 | 1221 | return self._gca_resource.description
|
1219 | 1222 |
|
| 1223 | + @property |
| 1224 | + def supported_export_formats( |
| 1225 | + self, |
| 1226 | + ) -> Dict[str, List[gca_model_compat.Model.ExportFormat.ExportableContent]]: |
| 1227 | + """The formats and content types in which this Model may be exported. |
| 1228 | + If empty, this Model is not available for export. |
| 1229 | +
|
| 1230 | + For example, if this model can be exported as a Tensorflow SavedModel and |
| 1231 | + have the artifacts written to Cloud Storage, the expected value would be: |
| 1232 | +
|
| 1233 | + {'tf-saved-model': [<ExportableContent.ARTIFACT: 1>]} |
| 1234 | + """ |
| 1235 | + return { |
| 1236 | + export_format.id: [ |
| 1237 | + gca_model_compat.Model.ExportFormat.ExportableContent(content) |
| 1238 | + for content in export_format.exportable_contents |
| 1239 | + ] |
| 1240 | + for export_format in self._gca_resource.supported_export_formats |
| 1241 | + } |
| 1242 | + |
1220 | 1243 | def __init__(
|
1221 | 1244 | self,
|
1222 | 1245 | model_name: str,
|
@@ -2030,3 +2053,142 @@ def list(
|
2030 | 2053 | location=location,
|
2031 | 2054 | credentials=credentials,
|
2032 | 2055 | )
|
| 2056 | + |
| 2057 | + @base.optional_sync() |
| 2058 | + def _wait_on_export(self, operation_future: operation.Operation, sync=True) -> None: |
| 2059 | + operation_future.result() |
| 2060 | + |
| 2061 | + def export_model( |
| 2062 | + self, |
| 2063 | + export_format_id: str, |
| 2064 | + artifact_destination: Optional[str] = None, |
| 2065 | + image_destination: Optional[str] = None, |
| 2066 | + sync: bool = True, |
| 2067 | + ) -> Dict[str, str]: |
| 2068 | + """Exports a trained, exportable Model to a location specified by the user. |
| 2069 | + A Model is considered to be exportable if it has at least one `supported_export_formats`. |
| 2070 | + Either `artifact_destination` or `image_destination` must be provided. |
| 2071 | +
|
| 2072 | + Usage: |
| 2073 | + my_model.export( |
| 2074 | + export_format_id='tf-saved-model' |
| 2075 | + artifact_destination='gs://my-bucket/models/' |
| 2076 | + ) |
| 2077 | +
|
| 2078 | + or |
| 2079 | +
|
| 2080 | + my_model.export( |
| 2081 | + export_format_id='custom-model' |
| 2082 | + image_destination='us-central1-docker.pkg.dev/projectId/repo/image' |
| 2083 | + ) |
| 2084 | +
|
| 2085 | + Args: |
| 2086 | + export_format_id (str): |
| 2087 | + Required. The ID of the format in which the Model must be exported. |
| 2088 | + The list of export formats that this Model supports can be found |
| 2089 | + by calling `Model.supported_export_formats`. |
| 2090 | + artifact_destination (str): |
| 2091 | + The Cloud Storage location where the Model artifact is to be |
| 2092 | + written to. Under the directory given as the destination a |
| 2093 | + new one with name |
| 2094 | + "``model-export-<model-display-name>-<timestamp-of-export-call>``", |
| 2095 | + where timestamp is in YYYY-MM-DDThh:mm:ss.sssZ ISO-8601 |
| 2096 | + format, will be created. Inside, the Model and any of its |
| 2097 | + supporting files will be written. |
| 2098 | +
|
| 2099 | + This field should only be set when, in [Model.supported_export_formats], |
| 2100 | + the value for the key given in `export_format_id` contains ``ARTIFACT``. |
| 2101 | + image_destination (str): |
| 2102 | + The Google Container Registry or Artifact Registry URI where |
| 2103 | + the Model container image will be copied to. Accepted forms: |
| 2104 | +
|
| 2105 | + - Google Container Registry path. For example: |
| 2106 | + ``gcr.io/projectId/imageName:tag``. |
| 2107 | +
|
| 2108 | + - Artifact Registry path. For example: |
| 2109 | + ``us-central1-docker.pkg.dev/projectId/repoName/imageName:tag``. |
| 2110 | +
|
| 2111 | + This field should only be set when, in [Model.supported_export_formats], |
| 2112 | + the value for the key given in `export_format_id` contains ``IMAGE``. |
| 2113 | + sync (bool): |
| 2114 | + Whether to execute this export synchronously. If False, this method |
| 2115 | + will be executed in concurrent Future and any downstream object will |
| 2116 | + be immediately returned and synced when the Future has completed. |
| 2117 | + Returns: |
| 2118 | + output_info (Dict[str, str]): |
| 2119 | + Details of the completed export with output destination paths to |
| 2120 | + the artifacts or container image. |
| 2121 | + Raises: |
| 2122 | + ValueError if model does not support exporting. |
| 2123 | +
|
| 2124 | + ValueError if invalid arguments or export formats are provided. |
| 2125 | + """ |
| 2126 | + |
| 2127 | + # Model does not support exporting |
| 2128 | + if not self.supported_export_formats: |
| 2129 | + raise ValueError(f"The model `{self.resource_name}` is not exportable.") |
| 2130 | + |
| 2131 | + # No destination provided |
| 2132 | + if not any((artifact_destination, image_destination)): |
| 2133 | + raise ValueError( |
| 2134 | + "Please provide an `artifact_destination` or `image_destination`." |
| 2135 | + ) |
| 2136 | + |
| 2137 | + export_format_id = export_format_id.lower() |
| 2138 | + |
| 2139 | + # Unsupported export type |
| 2140 | + if export_format_id not in self.supported_export_formats: |
| 2141 | + raise ValueError( |
| 2142 | + f"'{export_format_id}' is not a supported export format for this model. " |
| 2143 | + f"Choose one of the following: {self.supported_export_formats}" |
| 2144 | + ) |
| 2145 | + |
| 2146 | + content_types = gca_model_compat.Model.ExportFormat.ExportableContent |
| 2147 | + supported_content_types = self.supported_export_formats[export_format_id] |
| 2148 | + |
| 2149 | + if ( |
| 2150 | + artifact_destination |
| 2151 | + and content_types.ARTIFACT not in supported_content_types |
| 2152 | + ): |
| 2153 | + raise ValueError( |
| 2154 | + "This model can not be exported as an artifact in '{export_format_id}' format. " |
| 2155 | + "Try exporting as a container image by passing the `image_destination` argument." |
| 2156 | + ) |
| 2157 | + |
| 2158 | + if image_destination and content_types.IMAGE not in supported_content_types: |
| 2159 | + raise ValueError( |
| 2160 | + "This model can not be exported as a container image in '{export_format_id}' format. " |
| 2161 | + "Try exporting the model artifacts by passing a `artifact_destination` argument." |
| 2162 | + ) |
| 2163 | + |
| 2164 | + # Construct request payload |
| 2165 | + output_config = gca_model_service_compat.ExportModelRequest.OutputConfig( |
| 2166 | + export_format_id=export_format_id |
| 2167 | + ) |
| 2168 | + |
| 2169 | + if artifact_destination: |
| 2170 | + output_config.artifact_destination = gca_io_compat.GcsDestination( |
| 2171 | + output_uri_prefix=artifact_destination |
| 2172 | + ) |
| 2173 | + |
| 2174 | + if image_destination: |
| 2175 | + output_config.image_destination = gca_io_compat.ContainerRegistryDestination( |
| 2176 | + output_uri=image_destination |
| 2177 | + ) |
| 2178 | + |
| 2179 | + _LOGGER.log_action_start_against_resource("Exporting", "model", self) |
| 2180 | + |
| 2181 | + operation_future = self.api_client.export_model( |
| 2182 | + name=self.resource_name, output_config=output_config |
| 2183 | + ) |
| 2184 | + |
| 2185 | + _LOGGER.log_action_started_against_resource_with_lro( |
| 2186 | + "Export", "model", self.__class__, operation_future |
| 2187 | + ) |
| 2188 | + |
| 2189 | + # Block before returning |
| 2190 | + self._wait_on_export(operation_future=operation_future, sync=sync) |
| 2191 | + |
| 2192 | + _LOGGER.log_action_completed_against_resource("model", "exported", self) |
| 2193 | + |
| 2194 | + return json_format.MessageToDict(operation_future.metadata.output_info._pb) |
0 commit comments