diff --git a/automl/package.json b/automl/package.json index 5789b78810e..6fe011a2a6e 100644 --- a/automl/package.json +++ b/automl/package.json @@ -14,9 +14,17 @@ "**/*.test.js" ] }, - "scripts": {}, + "scripts": { + "test": "mocha --timeout 600000" + }, "dependencies": { - "@google-cloud/automl": "^0.1.3" + "@google-cloud/automl": "^0.1.3", + "chai": "^4.2.0", + "execa": "^1.0.0", + "mathjs": "^5.5.0", + "yargs": "^13.2.1" }, - "devDependencies": {} + "devDependencies": { + "mocha": "^6.0.1" + } } diff --git a/automl/system-test/.eslintrc.yml b/automl/system-test/.eslintrc.yml deleted file mode 100644 index c0289282a68..00000000000 --- a/automl/system-test/.eslintrc.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -rules: - node/no-unpublished-require: off - node/no-unsupported-features: off - no-empty: off diff --git a/automl/test/.eslintrc.yml b/automl/test/.eslintrc.yml new file mode 100644 index 00000000000..6db2a46c535 --- /dev/null +++ b/automl/test/.eslintrc.yml @@ -0,0 +1,3 @@ +--- +env: + mocha: true diff --git a/automl/test/automlVision.test.js b/automl/test/automlVision.test.js new file mode 100644 index 00000000000..16e2734afb1 --- /dev/null +++ b/automl/test/automlVision.test.js @@ -0,0 +1,139 @@ +/** + * Copyright 2018, 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const path = require('path'); +const {assert} = require('chai'); +const execa = require('execa'); + +const exec = async cmd => (await execa.shell(cmd)).stdout; +const cmdDataset = `node vision/automlVisionDataset.js`; +const cmdModel = `node vision/automlVisionModel.js`; +const cmdPredict = `node vision/automlVisionPredict.js`; + +const testDataSetName = `testDataSet`; +const dummyDataSet = `dummyDataSet`; +const testModelName = `dummyModel`; +const testImgPath = `./resources/`; +const sampleImage2 = path.join(testImgPath, `testImage2.jpg`); + +describe(`auto ml vision`, () => { + it.skip(`should create, list, and delete a dataset`, async () => { + // Check to see that this dataset does not yet exist + let output = await exec(`${cmdDataset} list-datasets`); + assert.strictEqual(output.includes(testDataSetName), false); + + // Create dataset + output = await exec(`${cmdDataset} create-dataset -n "${testDataSetName}"`); + const dataSetId = output + .split(`\n`)[1] + .split(`:`)[1] + .trim(); + assert.match(output, new RegExp(testDataSetName)); + + // Delete dataset + output = await exec(`${cmdDataset} delete-dataset -i "${dataSetId}"`); + assert.match(output, /Dataset deleted./); + }); + + // See : https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/vision/automl/model_test.py + // We make two models running this test, see hard-coded workaround below + it.skip(`should create a dataset, import data, and start making a model`, async () => { + // Check to see that this dataset does not yet exist + let output = await exec(`${cmdDataset} list-datasets`); + assert.strictEqual(output.includes(dummyDataSet), false); + + // Create dataset + output = await exec(`${cmdDataset} create-dataset -n "${dummyDataSet}"`); + const dataSetId = output + .split(`\n`)[1] + .split(`:`)[1] + .trim(); + assert.match(output, new RegExp(dummyDataSet)); + + // Import Data + output = await exec( + `${cmdDataset} import-data -i "${dataSetId}" -p "gs://nodejs-docs-samples-vcm/flowerTraindata20lines.csv"` + ); + assert.match(output, /Data imported./); + + // Check to make sure model doesn't already exist + output = await exec(`${cmdModel} list-models`); + assert.notMatch(output, new RegExp(testModelName)); + + // begin training dataset, getting operation ID for next operation + output = await exec(` + ${cmdModel} create-model -i "${dataSetId}" -m "${testModelName}" -t "2"`); + const operationName = output + .split(`\n`)[0] + .split(`:`)[1] + .split(`/`) + .pop() + .trim(); + assert.match(output, /Training started.../); + + // poll operation status, here confirming that operation is not complete yet + output = await exec( + `${cmdModel} get-operation-status -i "${dataSetId}" -o "${operationName}"` + ); + assert.match(output, /done: false/); + }); + + it.skip(`should display evaluation from prexisting model`, async () => { + const flowersModelId = `ICN723541179344731436`; + const flowersDisplayName = `flowersTest`; + + // Confirm dataset exists + let output = await exec(`${cmdDataset} list-datasets`); + assert.match(output, new RegExp(flowersDisplayName)); + + // List model evaluations, confirm model exists + output = await exec( + `${cmdModel} list-model-evaluations -a "${flowersModelId}"` + ); + + // Display evaluation + output = await exec( + `${cmdModel} display-evaluation -a "${flowersModelId}"` + ); + assert.match(output, /Model Precision/); + }); + + it.skip(`should run Prediction from prexisting model`, async () => { + const donotdeleteModelId = `ICN723541179344731436`; + const flowersDisplayName = `flowers`; + + // Confirm dataset exists + let output = await exec(`${cmdDataset} list-datasets`); + assert.match(output, new RegExp(flowersDisplayName)); + + // List model evaluations, confirm model exists + output = await exec( + `${cmdModel} list-model-evaluations -a "${donotdeleteModelId}"` + ); + // Run prediction on 'testImage.jpg' in resources folder + output = await exec( + `${cmdPredict} predict -i "${donotdeleteModelId}" -f "${sampleImage2}" -s "0.5"` + ); + assert.match(output, /dandelion/); + }); + + // List datasets + it(`should list datasets`, async () => { + const output = await exec(`${cmdDataset} list-datasets`); + assert.match(output, /List of datasets:/); + }); +}); diff --git a/automl/vision/automlVisionDataset.js b/automl/vision/automlVisionDataset.js new file mode 100644 index 00000000000..5a4d8ead89c --- /dev/null +++ b/automl/vision/automlVisionDataset.js @@ -0,0 +1,369 @@ +/** + * Copyright 2018, 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This application demonstrates how to perform basic operations on dataset + * with the Google AutoML Vision API. + * + * For more information, see the documentation at + * https://cloud.google.com/vision/automl/docs/ + */ + +`use strict`; + +async function createDataset( + projectId, + computeRegion, + datasetName, + multiLabel +) { + // [START automl_vision_create_dataset] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const datasetName = `name of the dataset to create, e.g. “myDataset”`; + // const multiLabel = `type of classification problem, true for multilabel and false for multiclass e.g. "false"`; + + // A resource that represents Google Cloud Platform location. + const projectLocation = client.locationPath(projectId, computeRegion); + + // Classification type is assigned based on multilabel value. + let classificationType = `MULTICLASS`; + if (multiLabel) { + classificationType = `MULTILABEL`; + } + + // Specify the text classification type for the dataset. + const datasetMetadata = { + classificationType: classificationType, + }; + + // Set dataset name and metadata. + const myDataset = { + displayName: datasetName, + imageClassificationDatasetMetadata: datasetMetadata, + }; + + // Create a dataset with the dataset metadata in the region. + const [dataset] = await client.createDataset({ + parent: projectLocation, + dataset: myDataset, + }); + // Display the dataset information. + console.log(`Dataset name: ${dataset.name}`); + console.log(`Dataset id: ${dataset.name.split(`/`).pop(-1)}`); + console.log(`Dataset display name: ${dataset.displayName}`); + console.log(`Dataset example count: ${dataset.exampleCount}`); + console.log(`Image Classification type:`); + console.log( + `\t ${dataset.imageClassificationDatasetMetadata.classificationType}` + ); + console.log(`Dataset create time:`); + console.log(`\tseconds: ${dataset.createTime.seconds}`); + console.log(`\tnanos: ${dataset.createTime.nanos}`); + // [END automl_vision_create_dataset] +} + +async function listDatasets(projectId, computeRegion, filter) { + // [START automl_vision_list_datasets] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const filter = `filter expressions, must specify field e.g. “imageClassificationModelMetadata:*”`; + + // A resource that represents Google Cloud Platform location. + const projectLocation = client.locationPath(projectId, computeRegion); + + // List all the datasets available in the region by applying filter. + const [datasets] = await client.listDatasets({ + parent: projectLocation, + filter: filter, + }); + console.log(`List of datasets:`); + datasets.forEach(dataset => { + console.log(`Dataset name: ${dataset.name}`); + console.log(`Dataset Id: ${dataset.name.split(`/`).pop(-1)}`); + console.log(`Dataset display name: ${dataset.displayName}`); + console.log(`Dataset example count: ${dataset.exampleCount}`); + console.log(`Image Classification type:`); + console.log( + `\t`, + dataset.imageClassificationDatasetMetadata.classificationType + ); + console.log(`Dataset create time: `); + console.log(`\tseconds: ${dataset.createTime.seconds}`); + console.log(`\tnanos: ${dataset.createTime.nanos}`); + console.log(`\n`); + }); + // [END automl_vision_list_datasets] +} + +async function getDataset(projectId, computeRegion, datasetId) { + // [START automl_vision_get_dataset] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const datasetId = `Id of the dataset`; + + // Get the full path of the dataset. + const datasetFullId = client.datasetPath(projectId, computeRegion, datasetId); + + // Get complete detail of the dataset. + const [dataset] = await client.getDataset({name: datasetFullId}); + // Display the dataset information. + console.log(`Dataset name: ${dataset.name}`); + console.log(`Dataset Id: ${dataset.name.split(`/`).pop(-1)}`); + console.log(`Dataset display name: ${dataset.displayName}`); + console.log(`Dataset example count: ${dataset.exampleCount}`); + console.log( + `Classification type: ${ + dataset.imageClassificationDatasetMetadata.classificationType + }` + ); + console.log(`Dataset create time: `); + console.log(`\tseconds: ${dataset.createTime.seconds}`); + console.log(`\tnanos: ${dataset.createTime.nanos}`); + // [END automl_vision_get_dataset] +} + +async function importData(projectId, computeRegion, datasetId, path) { + // [START automl_vision_import_data] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const datasetId = `Id of the dataset`; + // const path = `string or array of .csv paths in AutoML Vision CSV format, e.g. “gs://myproject/traindata.csv”;` + + // Get the full path of the dataset. + const datasetFullId = client.datasetPath(projectId, computeRegion, datasetId); + + // Get one or more Google Cloud Storage URI(s). + const inputUris = path.split(`,`); + const inputConfig = { + gcsSource: { + inputUris: inputUris, + }, + }; + + // Import the dataset from the input URI. + const [operation] = await client.importData({ + name: datasetFullId, + inputConfig: inputConfig, + }); + console.log(`Processing import...`); + + const [, , response] = await operation.promise(); + + // The final result of the operation. + if (response.done) { + console.log(`Data imported.`); + } + // [END automl_vision_import_data] +} + +async function exportData(projectId, computeRegion, datasetId, outputUri) { + // [START automl_vision_export_data] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const datasetId = `Id of the dataset`; + // const outputUri = `Google Cloud Storage URI for the export directory, e.g. “gs://myproject/output”;` + + // Get the full path of the dataset. + const datasetFullId = client.datasetPath(projectId, computeRegion, datasetId); + + // Set the output URI + const outputConfig = { + gcsDestination: { + outputUriPrefix: outputUri, + }, + }; + + // Export the data to the output URI. + const [operation] = await client.exportData({ + name: datasetFullId, + outputConfig: outputConfig, + }); + const [, , response] = await operation.promise(); + + // The final result of the operation. + if (response.done) { + console.log(`Data exported.`); + } + // [END automl_vision_export_data] +} + +async function deleteDataset(projectId, computeRegion, datasetId) { + // [START automl_vision_delete_dataset] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const datasetId = `Id of the dataset`; + + // Get the full path of the dataset. + const datasetFullId = client.datasetPath(projectId, computeRegion, datasetId); + + // Delete a dataset. + const [operation] = await client.deleteDataset({name: datasetFullId}); + const [, , response] = await operation.promise(); + // The final result of the operation. + if (response.done) { + console.log(`Dataset deleted.`); + } + // [END automl_vision_delete_dataset] +} + +require(`yargs`) // eslint-disable-line + .demand(1) + .options({ + computeRegion: { + alias: `c`, + type: `string`, + default: 'us-central1', + requiresArg: true, + description: `region name e.g. "us-central1"`, + }, + datasetName: { + alias: `n`, + type: `string`, + default: `testDataSet`, + requiresArg: true, + description: `Name of the Dataset`, + }, + datasetId: { + alias: `i`, + type: `string`, + requiresArg: true, + description: `Id of the dataset`, + }, + filter: { + alias: `f`, + default: `image_classification_dataset_metadata:*`, + type: `string`, + requiresArg: false, + description: `Name of the Dataset to search for`, + }, + multilabel: { + alias: `m`, + type: `string`, + default: false, + requiresArg: true, + description: + `Type of the classification problem, ` + + `False - MULTICLASS, True - MULTILABEL.`, + }, + outputUri: { + alias: `o`, + type: `string`, + requiresArg: true, + description: `URI (or local path) to export dataset`, + }, + path: { + alias: `p`, + type: `string`, + global: true, + default: `gs://nodejs-docs-samples-vcm/flowerTraindataMini.csv`, + requiresArg: true, + description: `URI or local path to input .csv, or array of .csv paths`, + }, + projectId: { + alias: `z`, + type: `string`, + default: process.env.GCLOUD_PROJECT, + requiresArg: true, + description: `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`, + }, + }) + .command(`create-dataset`, `creates a new Dataset`, {}, opts => + createDataset( + opts.projectId, + opts.computeRegion, + opts.datasetName, + opts.multilabel + ) + ) + .command(`list-datasets`, `list all Datasets`, {}, opts => + listDatasets(opts.projectId, opts.computeRegion, opts.filter) + ) + .command(`get-dataset`, `Get a Dataset`, {}, opts => + getDataset(opts.projectId, opts.computeRegion, opts.datasetId) + ) + .command(`delete-dataset`, `Delete a dataset`, {}, opts => + deleteDataset(opts.projectId, opts.computeRegion, opts.datasetId) + ) + .command(`import-data`, `Import labeled items into dataset`, {}, opts => + importData(opts.projectId, opts.computeRegion, opts.datasetId, opts.path) + ) + .command( + `export-data`, + `Export a dataset to a Google Cloud Storage Bucket`, + {}, + opts => + exportData( + opts.projectId, + opts.computeRegion, + opts.datasetId, + opts.outputUri + ) + ) + .example(`node $0 create-dataset -n "newDataSet"`) + .example(`node $0 list-datasets -f "image_classification_dataset_metadata:*"`) + .example(`node $0 get-dataset -i "DATASETID"`) + .example(`node $0 delete-dataset -i "DATASETID"`) + .example( + `node $0 import-data -i "dataSetId" -p "gs://myproject/mytraindata.csv"` + ) + .example( + `node $0 export-data -i "dataSetId" -o "gs://myproject/outputdestination.csv"` + ) + .wrap(120) + .recommendCommands() + .help() + .strict().argv; diff --git a/automl/vision/automlVisionModel.js b/automl/vision/automlVisionModel.js new file mode 100644 index 00000000000..492be1f312f --- /dev/null +++ b/automl/vision/automlVisionModel.js @@ -0,0 +1,550 @@ +/** + * Copyright 2018, 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This application demonstrates how to perform basic operations on dataset + * with the Google AutoML Vision API. + * + * For more information, see the documentation at + * https://cloud.google.com/vision/automl/docs/ + */ + +`use strict`; + +async function createModel( + projectId, + computeRegion, + datasetId, + modelName, + trainBudget +) { + // [START automl_vision_create_model] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const datasetId = `Id of the dataset`; + // const modelName = `Name of the model, e.g. "myModel"`; + // const trainBudget = `Budget for training model, e.g. 50`; + + // A resource that represents Google Cloud Platform location. + const projectLocation = client.locationPath(projectId, computeRegion); + + // Check train budget condition. + if (trainBudget === 0) { + trainBudget = {}; + } else { + trainBudget = {trainBudget: trainBudget}; + } + + // Set model name and model metadata for the dataset. + const myModel = { + displayName: modelName, + datasetId: datasetId, + imageClassificationModelMetadata: trainBudget, + }; + + // Create a model with the model metadata in the region. + const [operation, initialApiResponse] = await client.createModel({ + parent: projectLocation, + model: myModel, + }); + console.log(`Training operation name: `, initialApiResponse.name); + console.log(`Training started...`); + const [model] = await operation.promise(); + + // Retrieve deployment state. + let deploymentState = ``; + if (model.deploymentState === 1) { + deploymentState = `deployed`; + } else if (model.deploymentState === 2) { + deploymentState = `undeployed`; + } + + // Display the model information. + console.log(`Model name: ${model.name}`); + console.log(`Model id: ${model.name.split(`/`).pop(-1)}`); + console.log(`Model display name: ${model.displayName}`); + console.log(`Model create time:`); + console.log(`\tseconds: ${model.createTime.seconds}`); + console.log(`\tnanos: ${model.createTime.nanos}`); + console.log(`Model deployment state: ${deploymentState}`); + // [END automl_vision_create_model] +} + +async function getOperationStatus(operationFullId) { + // [START automl_vision_get_operation_status] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const operationFullId = `Full name of an operation, eg. “Projects//locations/us-central1/operations/ + + // Get the latest state of a long-running operation. + const [response] = await client.operationsClient.getOperation( + operationFullId + ); + console.log(`Operation status: `, response); + // [END automl_vision_get_operation_status] +} + +async function listModels(projectId, computeRegion, filter) { + // [START automl_vision_list_models] + const automl = require(`@google-cloud/automl`); + + const client = new automl.v1beta1.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const filter = `filter expressions, must specify field, e.g. “imageClassificationModelMetadata:*”`; + + // A resource that represents Google Cloud Platform location. + const projectLocation = client.locationPath(projectId, computeRegion); + + // List all the models available in the region by applying filter. + const [models] = await client.listModels({ + parent: projectLocation, + filter: filter, + }); + + // Display the model information. + console.log(`List of models:`); + models.forEach(model => { + console.log(`Model name: ${model.name}`); + console.log(`Model id: ${model.name.split(`/`).pop(-1)}`); + console.log(`Model display name: ${model.displayName}`); + console.log(`Model dataset id: ${model.datasetId}`); + if (model.modelMetadata === `translationModelMetadata`) { + console.log(`Translation model metadata:`); + console.log(`\tBase model: ${model.translationModelMetadata.baseModel}`); + console.log( + `\tSource language code: ${ + model.translationModelMetadata.sourceLanguageCode + }` + ); + console.log( + `\tTarget language code: ${ + model.translationModelMetadata.targetLanguageCode + }` + ); + } else if (model.modelMetadata === `textClassificationModelMetadata`) { + console.log( + `Text classification model metadata: ${ + model.textClassificationModelMetadata + }` + ); + } else if (model.modelMetadata === `imageClassificationModelMetadata`) { + console.log(`Image classification model metadata:`); + console.log( + `\tBase model id: ${model.imageClassificationModelMetadata.baseModelId}` + ); + console.log( + `\tTrain budget: ${model.imageClassificationModelMetadata.trainBudget}` + ); + console.log( + `\tTrain cost: ${model.imageClassificationModelMetadata.trainCost}` + ); + console.log( + `\tStop reason: ${model.imageClassificationModelMetadata.stopReason}` + ); + } + console.log(`Model create time:`); + console.log(`\tseconds: ${model.createTime.seconds}`); + console.log(`\tnanos: ${model.createTime.nanos}`); + console.log(`Model update time:`); + console.log(`\tseconds: ${model.updateTime.seconds}`); + console.log(`\tnanos: ${model.updateTime.nanos}`); + console.log(`Model deployment state: ${model.deploymentState}`); + console.log(`\n`); + }); + // [END automl_vision_list_models] +} + +async function getModel(projectId, computeRegion, modelId) { + // [START automl_vision_get_model] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const modelId = `id of the model, e.g. “ICN12345”`; + + // Get the full path of the model. + const modelFullId = client.modelPath(projectId, computeRegion, modelId); + + // Get complete detail of the model. + const [model] = await client.getModel({name: modelFullId}); + + // Display the model information. + console.log(`Model name: ${model.name}`); + console.log(`Model id: ${model.name.split(`/`).pop(-1)}`); + console.log(`Model display name: ${model.displayName}`); + console.log(`Model dataset id: ${model.datasetId}`); + if (model.modelMetadata === `translationModelMetadata`) { + console.log(`Translation model metadata:`); + console.log(`\tBase model: ${model.translationModelMetadata.baseModel}`); + console.log( + `\tSource language code: ${ + model.translationModelMetadata.sourceLanguageCode + }` + ); + console.log( + `\tTarget language code: ${ + model.translationModelMetadata.targetLanguageCode + }` + ); + } else if (model.modelMetadata === `textClassificationModelMetadata`) { + console.log( + `Text classification model metadata: ${ + model.textClassificationModelMetadata + }` + ); + } else if (model.modelMetadata === `imageClassificationModelMetadata`) { + console.log(`Image classification model metadata:`); + console.log( + `\tBase model id: ${model.imageClassificationModelMetadata.baseModelId}` + ); + console.log( + `\tTrain budget: ${model.imageClassificationModelMetadata.trainBudget}` + ); + console.log( + `\tTrain cost: ${model.imageClassificationModelMetadata.trainCost}` + ); + console.log( + `\tStop reason: ${model.imageClassificationModelMetadata.stopReason}` + ); + } + console.log(`Model create time:`); + console.log(`\tseconds: ${model.createTime.seconds}`); + console.log(`\tnanos: ${model.createTime.nanos}`); + console.log(`Model update time:`); + console.log(`\tseconds: ${model.updateTime.seconds}`); + console.log(`\tnanos: ${model.updateTime.nanos}`); + console.log(`Model deployment state: ${model.deploymentState}`); + // [END automl_vision_get_model] +} + +async function listModelEvaluations(projectId, computeRegion, modelId, filter) { + // [START automl_vision_list_model_evaluations] + const automl = require(`@google-cloud/automl`).v1beta1; + const util = require(`util`); + + const client = new automl.AutoMlClient(); + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const modelId = `id of the model, e.g. “ICN12345”`; + // const filter = `filter expressions, must specify field, e.g. “imageClassificationModelMetadata:*”`; + + // Get the full path of the model. + const modelFullId = client.modelPath(projectId, computeRegion, modelId); + + // List all the model evaluations in the model by applying filter. + const [elements] = await client.listModelEvaluations({ + parent: modelFullId, + filter: filter, + }); + elements.forEach(element => { + console.log(util.inspect(element, false, null)); + }); + // [END automl_vision_list_model_evaluations] +} + +async function getModelEvaluation( + projectId, + computeRegion, + modelId, + modelEvaluationId +) { + // [START automl_vision_get_model_evaluation] + const automl = require(`@google-cloud/automl`).v1beta1; + const util = require(`util`); + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const modelId = `id of the model, e.g. “ICN12345”`; + // const modelEvaluationId = `Id of your model evaluation, e.g “ICN12345” + + // Get the full path of the model evaluation. + const modelEvaluationFullId = client.modelEvaluationPath( + projectId, + computeRegion, + modelId, + modelEvaluationId + ); + + // Get complete detail of the model evaluation. + const [response] = await client.getModelEvaluation({ + name: modelEvaluationFullId, + }); + console.log(util.inspect(response, false, null)); + // [END automl_vision_get_model_evaluation] +} + +async function displayEvaluation(projectId, computeRegion, modelId, filter) { + // [START automl_vision_display_evaluation] + const automl = require(`@google-cloud/automl`).v1beta1; + const math = require(`mathjs`); + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const modelId = `id of the model, e.g. “ICN12345”`; + // const filter = `filter expressions, must specify field, e.g. “imageClassificationModelMetadata:*”`; + + // Get the full path of the model. + const modelFullId = client.modelPath(projectId, computeRegion, modelId); + + // List all the model evaluations in the model by applying filter. + const [response] = await client.listModelEvaluations({ + parent: modelFullId, + filter: filter, + }); + + response.forEach(async element => { + // There is evaluation for each class in a model and for overall model. + // Get only the evaluation of overall model. + if (!element.annotationSpecId) { + const modelEvaluationId = element.name.split(`/`).pop(-1); + + // Resource name for the model evaluation. + const modelEvaluationFullId = client.modelEvaluationPath( + projectId, + computeRegion, + modelId, + modelEvaluationId + ); + + const [modelEvaluation] = await client.getModelEvaluation({ + name: modelEvaluationFullId, + }); + const classMetrics = modelEvaluation.classificationEvaluationMetrics; + const confidenceMetricsEntries = classMetrics.confidenceMetricsEntry; + + // Showing model score based on threshold of 0.5 + confidenceMetricsEntries.forEach(confidenceMetricsEntry => { + if (confidenceMetricsEntry.confidenceThreshold === 0.5) { + console.log( + `Precision and recall are based on a score threshold of 0.5` + ); + console.log( + `Model Precision: %`, + math.round(confidenceMetricsEntry.precision * 100, 2) + ); + console.log( + `Model Recall: %`, + math.round(confidenceMetricsEntry.recall * 100, 2) + ); + console.log( + `Model F1 score: %`, + math.round(confidenceMetricsEntry.f1Score * 100, 2) + ); + console.log( + `Model Precision@1: %`, + math.round(confidenceMetricsEntry.precisionAt1 * 100, 2) + ); + console.log( + `Model Recall@1: %`, + math.round(confidenceMetricsEntry.recallAt1 * 100, 2) + ); + console.log( + `Model F1 score@1: %`, + math.round(confidenceMetricsEntry.f1ScoreAt1 * 100, 2) + ); + } + }); + } + }); + // [END automl_vision_display_evaluation] +} + +async function deleteModel(projectId, computeRegion, modelId) { + // [START automl_vision_delete_model] + const automl = require(`@google-cloud/automl`).v1beta1; + + const client = new automl.AutoMlClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const modelId = `id of the model, e.g. “ICN12345”`; + + // Get the full path of the model. + const modelFullId = client.modelPath(projectId, computeRegion, modelId); + + // Delete a model. + const [operation] = await client.deleteModel({name: modelFullId}); + const [, , response] = await operation.promise(); + // The final result of the operation. + if (response.done) { + console.log(`Model deleted.`); + } + // [END automl_vision_delete_model] +} + +require(`yargs`) // eslint-disable-line + .demand(1) + .options({ + computeRegion: { + alias: `c`, + type: `string`, + default: 'us-central1', + requiresArg: true, + description: `region name e.g. "us-central1"`, + }, + datasetId: { + alias: `i`, + type: `string`, + requiresArg: true, + description: `Id of the dataset`, + }, + filter: { + alias: `f`, + default: ``, + type: `string`, + requiresArg: true, + description: `Name of the Dataset to search for`, + }, + modelName: { + alias: `m`, + type: `string`, + default: false, + requiresArg: true, + description: `Name of the model`, + }, + modelId: { + alias: `a`, + type: `string`, + default: ``, + requiresArg: true, + description: `Id of the model`, + }, + modelEvaluationId: { + alias: `e`, + type: `string`, + default: ``, + requiresArg: true, + description: `Id of the model evaluation`, + }, + operationFullId: { + alias: `o`, + type: `string`, + default: ``, + requiresArg: true, + description: `Full name of an operation`, + }, + projectId: { + alias: `z`, + type: `string`, + default: process.env.GCLOUD_PROJECT, + requiresArg: true, + description: `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`, + }, + trainBudget: { + alias: `t`, + type: `string`, + default: ``, + requiresArg: true, + description: `Budget for training the model`, + }, + }) + .command(`create-model`, `creates a new Model`, {}, opts => + createModel( + opts.projectId, + opts.computeRegion, + opts.datasetId, + opts.modelName, + opts.trainBudget + ) + ) + .command( + `get-operation-status`, + `Gets status of current operation`, + {}, + opts => getOperationStatus(opts.operationFullId) + ) + .command(`list-models`, `list all Models`, {}, opts => + listModels(opts.projectId, opts.computeRegion, opts.filter) + ) + .command(`get-model`, `Get a Model`, {}, opts => + getModel(opts.projectId, opts.computeRegion, opts.modelId) + ) + .command(`list-model-evaluations`, `List model evaluations`, {}, opts => + listModelEvaluations( + opts.projectId, + opts.computeRegion, + opts.modelId, + opts.filter + ) + ) + .command(`get-model-evaluation`, `Get model evaluation`, {}, opts => + getModelEvaluation( + opts.projectId, + opts.computeRegion, + opts.modelId, + opts.modelEvaluationId + ) + ) + .command(`display-evaluation`, `Display evaluation`, {}, opts => + displayEvaluation( + opts.projectId, + opts.computeRegion, + opts.modelId, + opts.filter + ) + ) + .command(`delete-model`, `Delete a Model`, {}, opts => + deleteModel(opts.projectId, opts.computeRegion, opts.modelId) + ) + .example(`node $0 create-model -i "datasetId" -m "myModelName" -t "2"`) + .example(`node $0 get-operation-status -i "datasetId" -o "OperationFullID"`) + .example(`node $0 list-models -f "image_classification_dataset_metadata:*"`) + .example(`node $0 get-model -a "ModelID"`) + .example(`node $0 list-model-evaluations -a "ModelID"`) + .example(`node $0 get-model-evaluation -a "ModelId" -e "ModelEvaluationID"`) + .example(`node $0 display-evaluation -a "ModelId"`) + .example(`node $0 delete-model -a "ModelID"`) + .wrap(120) + .recommendCommands() + .help() + .strict().argv; diff --git a/automl/vision/automlVisionPredict.js b/automl/vision/automlVisionPredict.js new file mode 100644 index 00000000000..532a412cc9b --- /dev/null +++ b/automl/vision/automlVisionPredict.js @@ -0,0 +1,136 @@ +/** + * Copyright 2018, 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * This application demonstrates how to perform basic operations on dataset + * with the Google AutoML Natural Language API. + * + * For more information, see the documentation at + * https://cloud.google.com/natural-language/automl/docs/ + */ + +'use strict'; + +async function predict( + projectId, + computeRegion, + modelId, + filePath, + scoreThreshold +) { + // [START automl_vision_predict] + const automl = require('@google-cloud/automl').v1beta1; + const fs = require('fs'); + + // Create client for prediction service. + const client = new automl.PredictionServiceClient(); + + /** + * TODO(developer): Uncomment the following line before running the sample. + */ + // const projectId = `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`; + // const computeRegion = `region-name, e.g. "us-central1"`; + // const modelId = `id of the model, e.g. “ICN12345”`; + // const filePath = `local text file path of content to be classified, e.g. "./resources/test.txt"`; + // const scoreThreshold = `value between 0.0 and 1.0, e.g. "0.5"'; + + // Get the full path of the model. + const modelFullId = client.modelPath(projectId, computeRegion, modelId); + + // Read the file content for prediction. + const content = fs.readFileSync(filePath, 'base64'); + + const params = {}; + + if (scoreThreshold) { + params.score_threshold = scoreThreshold; + } + + // Set the payload by giving the content and type of the file. + const payload = {}; + payload.image = {imageBytes: content}; + + // params is additional domain-specific parameters. + // currently there is no additional parameters supported. + const [response] = await client.predict({ + name: modelFullId, + payload: payload, + params: params, + }); + console.log(`Prediction results:`); + response.payload.forEach(result => { + console.log(`Predicted class name: ${result.displayName}`); + console.log(`Predicted class score: ${result.classification.score}`); + }); + // [END automl_vision_predict] +} + +require(`yargs`) // eslint-disable-line + .demand(1) + .options({ + computeRegion: { + alias: `c`, + type: `string`, + default: 'us-central1', + requiresArg: true, + description: `region name e.g. "us-central1"`, + }, + filePath: { + alias: `f`, + default: `./resources/testImage.jpg`, + type: `string`, + requiresArg: true, + description: `local text file path of the content to be classified`, + }, + modelId: { + alias: `i`, + //default: ``, + type: `string`, + requiresArg: true, + description: `Id of the model which will be used for text classification`, + }, + projectId: { + alias: `z`, + type: `string`, + default: process.env.GCLOUD_PROJECT, + requiresArg: true, + description: `The GCLOUD_PROJECT string, e.g. "my-gcloud-project"`, + }, + scoreThreshold: { + alias: `s`, + type: `string`, + default: `0.5`, + requiresArg: true, + description: + `A value from 0.0 to 1.0. When the model makes predictions for an image it will` + + `only produce results that have at least this confidence score threshold. Default is .5`, + }, + }) + .command(`predict`, `classify the content`, {}, opts => + predict( + opts.projectId, + opts.computeRegion, + opts.modelId, + opts.filePath, + opts.scoreThreshold + ) + ) + .example( + `node $0 predict -i "modelId" -f "./resources/testImage.jpg" -s "0.5"` + ) + .wrap(120) + .recommendCommands() + .help() + .strict().argv; diff --git a/automl/vision/resources/flowerTraindata20lines.csv b/automl/vision/resources/flowerTraindata20lines.csv new file mode 100644 index 00000000000..a0617c06e9d --- /dev/null +++ b/automl/vision/resources/flowerTraindata20lines.csv @@ -0,0 +1,19 @@ +gs://nodejs-docs-samples-vcm/daisy/14354051035_1037b30421_n.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/5586977262_6b24412805_n.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/14707111433_cce08ee007.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/2488902131_3417698611_n.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/506348009_9ecff8b6ef.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/2331133004_582772d58f_m.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/4511693548_20f9bd2b9c_m.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/12601254324_3cb62c254a_m.jpg,daisy +gs://nodejs-docs-samples-vcm/daisy/2473825306_62fd5f8785_n.jpg,daisy +gs://nodejs-docs-samples-vcm/dandelion/18089878729_907ed2c7cd_m.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/284497199_93a01f48f6.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/3554992110_81d8c9b0bd_m.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/4558536575_d43a611bd4_n.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/15782158700_3b9bf7d33e_m.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/8754822932_948afc7cef.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/2229906591_e953785d13.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/3991962484_085ba2da94.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/4500964841_b1142b50fb_n.jpg,dandelion +gs://nodejs-docs-samples-vcm/dandelion/7808545612_546cfca610_m.jpg,dandelion diff --git a/automl/vision/resources/testImage.jpg b/automl/vision/resources/testImage.jpg new file mode 100644 index 00000000000..852c4635210 Binary files /dev/null and b/automl/vision/resources/testImage.jpg differ diff --git a/automl/vision/resources/testImage2.jpg b/automl/vision/resources/testImage2.jpg new file mode 100644 index 00000000000..674a1aa4ea7 Binary files /dev/null and b/automl/vision/resources/testImage2.jpg differ