license import { InferenceModel , io , ModelPredictConfig , NamedTensorMap , Tensor } from '@tensorflow/tfjs-core' ; import * as tensorflow from '../data/compiled_api' ; import { NamedTensorsMap , TensorInfo } from '../data/types' ; import { OperationMapper } from '../operations/operation_mapper' ; import { GraphExecutor } from './graph_executor' ; import { ResourceManager } from './resource_manager' ; export const TFHUB_SEARCH_PARAM = '?tfjs-format=file' ; export const DEFAULT_MODEL_NAME = 'model.json' ; doc export class GraphModel implements InferenceModel { private executor : GraphExecutor ; private version = 'n/a' ; private handler : io . IOHandler ; private artifacts : io . ModelArtifacts ; private initializer : GraphExecutor ; private resourceManager : ResourceManager ; private signature : tensorflow . ISignatureDef ; get modelVersion ( ) : string { return this . version ; } get inputNodes ( ) : string [ ] { return this . executor . inputNodes ; } get outputNodes ( ) : string [ ] { return this . executor . outputNodes ; } get inputs ( ) : TensorInfo [ ] { return this . executor . inputs ; } get outputs ( ) : TensorInfo [ ] { return this . executor . outputs ; } get weights ( ) : NamedTensorsMap { return this . executor . weightMap ; } get metadata ( ) : { } { return this . artifacts . userDefinedMetadata ; } get modelSignature ( ) : { } { return this . signature ; } param modelUrl param weightManifestUrl param requestOption param onProgress constructor ( private modelUrl , private loadOptions = { } ) { if ( loadOptions == null ) { this . loadOptions = { } ; } this . resourceManager = new ResourceManager ( ) ; } private findIOHandler ( ) { const path = this . modelUrl ; if ( ( path as io . IOHandler ) . load != null ) { this . handler = path as io . IOHandler ; } else if ( this . loadOptions . requestInit != null ) { this . handler = io . browserHTTPRequest ( path as string , this . loadOptions ) ; } else { const handlers = io . getLoadHandlers ( path as string , this . loadOptions ) ; if ( handlers . length === 0 ) { handlers . push ( io . browserHTTPRequest ( path as string , this . loadOptions ) ) ; } else if ( handlers . length > 1 ) { throw new Error ( `Found more than one (${ handlers . length }) load handlers for ` + `URL '${ [ path ] }'` ) ; } this . handler = handlers [ 0 ] ; } } /**
     * Loads the model and weight files, construct the in memory weight map and
     * compile the inference graph.
     */ async load ( ) { this . findIOHandler ( ) ; if ( this . handler . load == null ) { throw new Error ( 'Cannot proceed with model loading because the IOHandler provided ' + 'does not have the `load` method implemented.' ) ; } const artifacts = await this . handler . load ( ) ; return this . loadSync ( artifacts ) ; } doc loadSync ( artifacts ) { this . artifacts = artifacts ; const graph = this . artifacts . modelTopology as tensorflow . IGraphDef ; let signature ; if ( this . artifacts . userDefinedMetadata != null && this . artifacts . userDefinedMetadata . signature != null ) { signature = ( this . artifacts . userDefinedMetadata as any ) . signature as tensorflow . ISignatureDef ; } else { signature = this . artifacts . signature ; } this . signature = signature ; this . version = `${ graph . versions . producer }.${ graph . versions . minConsumer }` ; const weightMap = io . decodeWeights ( this . artifacts . weightData , this . artifacts . weightSpecs ) ; this . executor = new GraphExecutor ( OperationMapper . Instance . transformGraph ( graph , this . signature ) ) ; this . executor . weightMap = this . convertTensorMapToTensorsMap ( weightMap ) ; this . executor . resourceManager = this . resourceManager ; if ( artifacts . modelInitializer != null && ( artifacts . modelInitializer as tensorflow . IGraphDef ) . node != null ) { const initializer = OperationMapper . Instance . transformGraph ( artifacts . modelInitializer ) ; this . initializer = new GraphExecutor ( initializer ) ; this . initializer . weightMap = this . executor . weightMap ; this . initializer . resourceManager = this . resourceManager ; this . initializer . executeAsync ( { } , [ ] ) ; } return true ; } param handlerOrURL param config returns doc async save ( handlerOrURL , config ? ) { if ( typeof handlerOrURL === 'string' ) { const handlers = io . getSaveHandlers ( handlerOrURL ) ; if ( handlers . length === 0 ) { throw new Error ( `Cannot find any save handlers for URL '${ handlerOrURL }'` ) ; } else if ( handlers . length > 1 ) { throw new Error ( `Found more than one (${ handlers . length }) save handlers for ` + `URL '${ handlerOrURL }'` ) ; } handlerOrURL = handlers [ 0 ] ; } if ( handlerOrURL . save == null ) { throw new Error ( 'GraphModel.save() cannot proceed because the IOHandler ' + 'provided does not have the `save` attribute defined.' ) ; } return handlerOrURL . save ( this . artifacts ) ; } param input see GraphModel . inputNodes *
     * You can also feed any intermediate nodes using the NamedTensorMap as the
     * input type. For example, given the graph
     *    InputNode => Intermediate => OutputNode,
     * you can execute the subgraph Intermediate => OutputNode by calling
     *    model.execute('IntermediateNode' : tf.tensor(...));
     *
     * This is useful for models that uses tf.dynamic_rnn, where the intermediate
     * state needs to be fed manually.
     *
     * For batch inference execution, the tensors for each input need to be
     * concatenated together. For example with mobilenet, the required input shape
     * is [1, 244, 244, 3], which represents the [batch, height, width, channel].
     * If we are provide a batched data of 100 images, the input tensor should be
     * in the shape of [100, 244, 244, 3].
     *
     *  param config returns doc predict ( inputs , config ? ) { return this . execute ( inputs , this . outputNodes ) ; } private normalizeInputs ( inputs ) { if ( ! ( inputs instanceof Tensor ) && ! Array . isArray ( inputs ) ) { return inputs ; } inputs = Array . isArray ( inputs ) ? inputs : [ inputs ] ; if ( inputs . length !== this . inputNodes . length ) { throw new Error ( 'Input tensor count mismatch,' + `the graph model has ${ this . inputNodes . length } placeholders, ` + `while there are ${ inputs . length } input tensors.` ) ; } return this . inputNodes . reduce ( ( map , inputName , i ) => { map [ inputName ] = ( inputs as Tensor [ ] ) [ i ] ; return map ; } , { } as NamedTensorMap ) ; } private normalizeOutputs ( outputs ) { outputs = outputs || this . outputNodes ; return ! Array . isArray ( outputs ) ? [ outputs ] : outputs ; } param inputs param outputs returns doc execute ( inputs , outputs ? ) { inputs = this . normalizeInputs ( inputs ) ; outputs = this . normalizeOutputs ( outputs ) ; const result = this . executor . execute ( inputs , outputs ) ; return result . length > 1 ? result : result [ 0 ] ; } param inputs param outputs returns doc async executeAsync ( inputs , outputs ? ) { inputs = this . normalizeInputs ( inputs ) ; outputs = this . normalizeOutputs ( outputs ) ; const result = await this . executor . executeAsync ( inputs , outputs ) ; return result . length > 1 ? result : result [ 0 ] ; } doc getIntermediateTensors ( ) { return this . executor . getIntermediateTensors ( ) ; } doc disposeIntermediateTensors ( ) { this . executor . disposeIntermediateTensors ( ) ; } private convertTensorMapToTensorsMap ( map ) { return Object . keys ( map ) . reduce ( ( newMap , key ) => { newMap [ key ] = [ map [ key ] ] ; return newMap ; } , { } ) ; } doc dispose ( ) { this . executor . dispose ( ) ; if ( this . initializer ) { this . initializer . dispose ( ) ; } this . resourceManager . dispose ( ) ; } } param modelUrl param options doc export async function loadGraphModel ( modelUrl , options = { } ) { if ( modelUrl == null ) { throw new Error ( 'modelUrl in loadGraphModel() cannot be null. Please provide a url ' + 'or an IOHandler that loads the model' ) ; } if ( options == null ) { options = { } ; } if ( options . fromTFHub ) { if ( ( modelUrl as io . IOHandler ) . load == null ) { if ( ! ( modelUrl as string ) . endsWith ( '/' ) ) { modelUrl = ( modelUrl as string ) + '/' ; } modelUrl = `${ modelUrl }${ DEFAULT_MODEL_NAME }${ TFHUB_SEARCH_PARAM }` ; } } const model = new GraphModel ( modelUrl , options ) ; await model . load ( ) ; return model ; }